<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Website Fingerprinting Lab</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      body {
        font-family: Arial, Helvetica, sans-serif;
        padding: 64px;
      }

      h1 {
        font-size: 32px;
        margin-bottom: 16px;
      }

      p {
        margin-bottom: 8px;
      }

      #buttons {
        margin-bottom: 16px;
      }

      button {
        background: #fff;
        border: 2px solid #3498db;
        border-radius: 4px;
        color: #3498db;
        cursor: pointer;
        display: inline-block;
        font-size: 16px;
        margin-right: 16px;
        padding: 8px 16px;
      }

      button.disabled {
        background: #ccc;
        border-color: #ccc;
        color: #666;
        cursor: default;
      }

      .trace {
        margin-bottom: 16px;
      }
    </style>
  </head>
  <body>
    <h1>Website Fingerprinting Lab</h1>
    <div id="buttons">
      <button id="collect-trace">Collect trace</button>
      <button id="download-traces">Download traces</button>
    </div>
    <div id="traces"></div>
    <script src="https://d3js.org/d3.v6.js"></script>
    <script type="text/javascript">
      const worker = new Worker("worker.js");

      const collectTraceButton = document.getElementById("collect-trace");
      const downloadTracesButton = document.getElementById("download-traces");

      // Default values for when the automation script isn't being used. When
      // the script is in use, these values will get overwritten.
      window.trace_length = 5000;
      window.using_automation_script = false;

      window.recording = false;
      window.traces = [];

      let traceIds = [];

      worker.onmessage = (e) => {
        window.recording = false;

        const trace = JSON.parse(e.data);
        window.traces.push(trace);

        if (window.using_automation_script) {
          // Don't display traces when automation script is in use
          return;
        }

        // Create new trace div
        const parent = document.getElementById("traces");
        const div = document.createElement("div");
        const traceId = "a" + Math.random().toString().substring(2, 10);
        div.setAttribute("id", traceId);
        div.className = "trace";
        parent.appendChild(div);
        traceIds.push(traceId);

        // Trace dimensions
        const width = parent.getBoundingClientRect().width;
        const height = 64;

        // Create div for new trace
        const svg = d3
          .select("#" + traceId)
          .append("svg")
          .attr("width", width)
          .attr("height", height);

        // Find largest value across all traces
        const maxVal = d3.max(window.traces, (d) => d3.max(d));

        for (let i = 0; i < window.traces.length; i++) {
          // Re-visualize all traces each time in case maxVal changes
          const x = d3
            .scaleLinear()
            .domain([0, window.traces[i].length])
            .range([0, width]);

          const color = d3
            .scaleQuantize()
            .range(["#0d0887", "#7e03a8", "#cc4778", "#f89540", "#f0f921"])
            .domain([0, maxVal]);

          svg
            .selectAll()
            .data(window.traces[i].map((x, i) => ({ index: i, value: x })))
            .join("rect")
            .attr("x", (d) => x(d.index))
            .attr("y", 0)
            .attr("width", x(1))
            .attr("height", height)
            .style("fill", (d) => color(d.value));
        }

        // Reset UI
        collectTraceButton.innerText = "Collect trace";
        collectTraceButton.className = "";
      };

      function collectTrace() {
        collectTraceButton.innerText = "Collecting trace...";
        collectTraceButton.className = "disabled";
        window.recording = true;

        worker.postMessage({
          type: "start",
          trace_length: window.trace_length,
        });
      }

      collectTraceButton.onclick = () => {
        if (window.recording) return;

        window.recording = true;
        collectTraceButton.innerText = "Starting in 3...";
        collectTraceButton.className = "disabled";

        setTimeout(() => {
          collectTraceButton.innerText = "Starting in 2...";

          setTimeout(() => {
            collectTraceButton.innerText = "Starting in 1...";
            setTimeout(collectTrace, 1000);
          }, 1000);
        }, 1000);
      };

      downloadTracesButton.onclick = () => {
        const blob = new Blob([JSON.stringify({ traces: window.traces })], {
          type: "application/json",
        });

        const url = URL.createObjectURL(blob);

        const elem = document.createElement("a");
        elem.href = url;
        elem.download = "traces.json";
        document.body.appendChild(elem);

        elem.click();
        document.body.removeChild(elem);
      };
    </script>
  </body>
</html>