PageSourceSearch

https://gmpranav.github.io/src-splits-generator/app.js

js gmpranav.github.io collected 2026-10-03 09:14:59 UTC 3,648 bytes, 133 lines download raw bytes

1const textarea = document.getElementById("markdown");
2const preview = document.getElementById("preview");
3
4populateSrc(
5  `## Load a splits file to generate timestamps
6  \nYou can copy the source and paste it under your speedrun.com run description.`
7);
8
9function handleCopyLocation() {
10  navigator.clipboard.writeText(textarea.value);
11  document.getElementById("copy-tooltip").style.visibility = "visible";
12  setTimeout(() => {
13    document.getElementById("copy-tooltip").style.visibility = "hidden";
14  }, 1000);
15}
16
17function populateSrc(value) {
18  textarea.value = value;
19  populatePreview(value);
20}
21
22function populatePreview(value) {
23  preview.innerHTML = marked.parse(value.replaceAll(" ", " "));
24}
25
26function onloadSplitsFile(e) {
27  var file = e.target.files[0];
28  if (file) {
29    readFile(file);
30  }
31}
32
33function onChangeSettings() {
34  var file = document.getElementById("fileInput").files[0];
35  if (file) {
36    readFile(file);
37  }
38}
39
40function readFile(file) {
41  let reader = new FileReader();
42  reader.readAsText(file, "UTF-8");
43  reader.onload = function (evt) {
44    parseXML(evt.target.result);
45  };
46  reader.onerror = function (evt) {
47    populateSrc("error reading file");
48  };
49}
50
51function parseStime(stime) {
52  const [hh, mm, ssFraction] = stime.split(":");
53  return (
54    parseInt(hh) * 3600000 +
55    parseInt(mm) * 60000 +
56    parseFloat(ssFraction) * 1000
57  );
58}
59
60function formatMilliseconds(ms, decimals) {
61  const factor = 10 ** decimals;
62  const rounded = Math.round(ms * factor) / factor;
63
64  let totalSeconds = Math.floor(rounded / 1000);
65  let fractional = Math.round((rounded % 1000) * factor / 1000);
66
67  if (fractional === factor) {
68    totalSeconds += 1;
69    fractional = 0;
70  }
71
72  const hours = Math.floor(totalSeconds / 3600);
73  const minutes = Math.floor((totalSeconds % 3600) / 60);
74  const seconds = totalSeconds % 60;
75
76  const pad = (n, len = 2) => n.toString().padStart(len, "0");
77
78  let time =
79    hours > 0
80      ? `${hours}:${pad(minutes)}:${pad(seconds)}`
81      : minutes > 0
82        ? `${minutes}:${pad(seconds)}`
83        : `${seconds}`;
84
85  if (decimals > 0) {
86    time += `.${fractional.toString().padStart(decimals, "0")}`;
87  }
88
89  return time;
90}
91
92function parseXML(xmlString) {
93  let parser = new DOMParser();
94  let xmlDoc = parser.parseFromString(xmlString, "text/xml");
95
96  const spaceValue = parseInt(document.getElementById("spacing").value);
97  const decimals = parseInt(document.getElementById("decimals").value);
98  const timingMethod = document.getElementById("timing-method").value;
99
100  let result = "";
101  let count = 1;
102  let prevTime = 0;
103  result += "## [Time Splits](https://gmpranav.github.io/src-splits-generator/)\n\n";
104  const spaceStr = " ".repeat(spaceValue);
105  result += `| **#** | | **Name** | ${spaceStr}| **Finished At** | ${spaceStr}| **Split** |\n`;
106  result += "|---|---|---|---|:---:|---|:---:|\n";
107
108  xmlDoc.querySelectorAll("Run Segments Segment").forEach((segment) => {
109    const splitName = segment
110      .querySelector("Name")
111      .textContent.split("}")
112      .pop()
113      .replace(/^(\s|-)+/, "");
114    const realTime = segment.querySelector(
115      `SplitTimes SplitTime[name="Personal Best"] ${timingMethod}`
116    )?.textContent;
117
118    if (realTime) {
119      const parsedTime = parseStime(realTime);
120      const splitTime = parsedTime - prevTime;
121
122      result += `| ${count} || ${splitName} || ${formatMilliseconds(parsedTime, decimals)} || ${formatMilliseconds(splitTime, decimals)} |\n`;
123      prevTime = parsedTime;
124    }
125    else {
126      result += `| ${count} || ${splitName} || - || - |\n`;
127      prevTime = "-";
128    }
129    count++;
130  });
131
132  populateSrc(result);
133}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.