<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Virtual Metro</title> <style type="text/css"> #mainsvg { width: 100vw; height: 100vh; max-width: 100vw; max-height: 100vh; } #topbar, #botbar { position: absolute; z-index: 999; font-size: small; } #topbar { left: 0; top: 0; } #botbar { right: 0; bottom: 0; text-align: right; } body, html { color: #ccc; background-color: black; margin: 0; padding: 0; overflow: hidden; } a, a:active, a:visited { color: #99f; } </style> </head> <body> <div id="topbar">Stop <span id="stopinfo">?</span> [<a href="stations">change</a>] <span id="platinfo">Platform ?</span> [<a href="#" onclick="return changePlatform();">change</a>] Style <span id="styleinfo">?</span> [change: <a href="#" onclick="return changeStyle(1);">1</a>, <a href="#" onclick="return changeStyle(2);">2</a>]</div> <div id="botbar">By RunasSudo · AGPLv3 · <a href="https://gitlab.com/RunasSudo/virtual-metro">Source Code</a> · Data from PTV licensed under CC BY 4.0</div> <script src="https://cdnjs.cloudflare.com/ajax/libs/nunjucks/3.0.1/nunjucks.min.js"></script> <script> function getQueryVariable(variable, def) { var vars = window.location.search.substring(1).split("&"); for (var i = 0; i < vars.length; i++) { var bits = vars[i].split("="); if (decodeURIComponent(bits[0]) == variable) { return decodeURIComponent(bits[1]); } } return def; } var stop_id = parseInt(getQueryVariable("stop_id", 1099)); var plat_id = parseInt(getQueryVariable("plat_id", 1)); var template_id = parseInt(getQueryVariable("template_id", 1)); document.getElementById("stopinfo").innerText = stop_id; document.getElementById("platinfo").innerText = (plat_id == 0) ? "All Platforms" : ("Platform " + plat_id); document.getElementById("styleinfo").innerText = template_id; var svg = null; var svg_time = null; var template = null; var time_offset = null; function start() { svg = document.getElementById("mainsvg").getSVGDocument().querySelector("svg"); template = svg.innerHTML; svg.innerHTML = nunjucks.renderString(template, {"dest": "Loading..."}); tickTrains(); window.setInterval(tickTrains, 30000); } function tickTrains() { var xhr = new XMLHttpRequest(); xhr.addEventListener("load", function() { var result = JSON.parse(xhr.responseText); svg.innerHTML = nunjucks.renderString(template, result); svg_time = document.getElementById("mainsvg").getSVGDocument().querySelector("#time tspan"); document.getElementById("stopinfo").innerText = stop_id + " (" + result.stop_name + ")"; if (time_offset === null) { time_offset = result.time_offset; window.setInterval(tickTime, 1000); } tickTime(); }); xhr.open("GET", "latest?stop_id=" + stop_id + "&plat_id=" + plat_id); xhr.send(); } function tickTime() { var time = new Date(new Date().getTime() + time_offset * 1000); var hour = time.getUTCHours() % 12; if (hour === 0) hour = 12; var minute = time.getUTCMinutes(); if (minute < 10) minute = "0" + minute; var second = time.getUTCSeconds(); if (second < 10) second = "0" + second; svg_time.innerHTML = hour + ":" + minute + ":" + second; } function changePlatform() { var new_plat = window.prompt("Enter new platform number, or 0 for all platforms:", plat_id); if (new_plat !== null) { new_plat = parseInt(new_plat); window.location = "?stop_id=" + stop_id + "&plat_id=" + new_plat + "&template_id=" + template_id; return false; } } function changeStyle(new_style) { window.location = "?stop_id=" + stop_id + "&plat_id=" + plat_id + "&template_id=" + new_style; return false; } // Create SVG var svg_object = document.createElement("object"); svg_object.setAttribute("id", "mainsvg"); svg_object.setAttribute("data", "static/template" + template_id + ".svg"); svg_object.setAttribute("type", "image/svg+xml"); svg_object.setAttribute("onload", "start();"); document.querySelector("body").insertBefore(svg_object, document.querySelector("#botbar")); </script> </body> </html>