mirror of
https://github.com/lax1dude/crashreport-viewer.git
synced 2024-12-21 13:44:09 -08:00
made the thing
This commit is contained in:
commit
e138dd039c
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
27
LICENSE
Normal file
27
LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2022, lax1dude
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of {{ project }} nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
## Crash Report Viewer
|
||||
|
||||
web application, reads a "crash report" and deobfuscates javascript stack traces within it using a source map
|
||||
|
||||
WIP, to be used for eaglercraft
|
1
app/22w40a.json
Normal file
1
app/22w40a.json
Normal file
File diff suppressed because one or more lines are too long
1
app/22w41a.json
Normal file
1
app/22w41a.json
Normal file
File diff suppressed because one or more lines are too long
1
app/22w41b.json
Normal file
1
app/22w41b.json
Normal file
File diff suppressed because one or more lines are too long
133
app/crashviewer.css
Normal file
133
app/crashviewer.css
Normal file
|
@ -0,0 +1,133 @@
|
|||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro&family=Ubuntu:wght@400;700&display=swap');
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
margin-block-start: 0px;
|
||||
margin-block-end: 0px;
|
||||
}
|
||||
|
||||
body {
|
||||
color: white;
|
||||
background-color: black;
|
||||
margin: 0px;
|
||||
padding: 30px;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(100vw - 60px);
|
||||
height: calc(100vh - 60px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#titleRow {
|
||||
flex-grow: 0;
|
||||
padding-left: 1px;
|
||||
}
|
||||
|
||||
.containerHead {
|
||||
margin-top: 20px;
|
||||
border: 10px solid #666666;
|
||||
padding: 10px;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.containerBody {
|
||||
border-left: 10px solid #666666;
|
||||
border-right: 10px solid #666666;
|
||||
border-bottom: 10px solid #666666;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#sourceMaps {
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
font-size: 20px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.toggleShow {
|
||||
font-size: 20px;
|
||||
margin-left: 20px;
|
||||
padding: 3px 0px 4px 0px;
|
||||
color: #BBBBBB;
|
||||
background-color: black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggleShow:hover {
|
||||
color: #FFFFAA;
|
||||
}
|
||||
|
||||
.toggleShowDisabled, .toggleShowDisabled:hover {
|
||||
color: #888888 !important;
|
||||
padding: 3px 0px 0px 0px !important;
|
||||
border-bottom: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.toggleSelected, .toggleSelected:hover {
|
||||
padding: 3px 0px 2px 0px;
|
||||
color:white;
|
||||
border-bottom: 2px dashed white;
|
||||
}
|
||||
|
||||
#inputTextArea {
|
||||
margin: 0px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
resize: none;
|
||||
color: white;
|
||||
background-color: black;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-size: 18px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#outputTextArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#outputContent {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-size: 18px;
|
||||
padding: 10px;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
#fetchingMessage {
|
||||
font-size: 20px;
|
||||
margin-left: 20px;
|
||||
padding: 4px 0px;
|
||||
}
|
||||
|
||||
.darkModeScrollbar::-webkit-scrollbar, .darkModeScrollbar::-webkit-scrollbar-track {
|
||||
background-color: black;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.darkModeScrollbar::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.darkModeScrollbar::-webkit-scrollbar-thumb, .darkModeScrollbar::-webkit-scrollbar-corner {
|
||||
background-color: #BBBBBB;
|
||||
}
|
||||
|
||||
.darkModeScrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: white;
|
||||
}
|
457
app/crashviewer.js
Normal file
457
app/crashviewer.js
Normal file
|
@ -0,0 +1,457 @@
|
|||
"use strict";
|
||||
|
||||
const sourceMap = window.sourceMap;
|
||||
|
||||
sourceMap.SourceMapConsumer.initialize({ "lib/mappings.wasm": "mappings.wasm" });
|
||||
|
||||
var appElements = null;
|
||||
|
||||
const sourceMapURLs = new Map();
|
||||
const sourceMapLoaded = new Map();
|
||||
|
||||
var isShowingOriginal = true;
|
||||
var hasInput = false;
|
||||
|
||||
var dotsInterval = -1;
|
||||
var dotsCounter = 1;
|
||||
|
||||
function hideDots() {
|
||||
if(dotsInterval !== -1) {
|
||||
clearInterval(dotsInterval);
|
||||
dotsInterval = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function sourceMapListLoaded(jsonFile) {
|
||||
var selector = appElements.sourceMaps;
|
||||
selector.disabled = false;
|
||||
while(selector.firstChild) {
|
||||
selector.removeChild(selector.firstChild);
|
||||
}
|
||||
const mapsList = jsonFile.sourceMaps;
|
||||
if(mapsList.length > 0) {
|
||||
var customOption = document.createElement("option");
|
||||
customOption.value = "_custom";
|
||||
customOption.appendChild(document.createTextNode("upload..."));
|
||||
selector.appendChild(customOption);
|
||||
for(var i = 0; i < mapsList.length; ++i) {
|
||||
var name = mapsList[i].name;
|
||||
sourceMapURLs.set(name, mapsList[i].url);
|
||||
var newOption = document.createElement("option");
|
||||
newOption.value = name;
|
||||
newOption.appendChild(document.createTextNode(name));
|
||||
selector.appendChild(newOption);
|
||||
}
|
||||
selector.value = mapsList[0].name;
|
||||
}
|
||||
hideDots();
|
||||
appElements.fetchingMessage.style.display = "none";
|
||||
appElements.inputTextArea.style.display = "block";
|
||||
appElements.outputTextArea.style.display = "none";
|
||||
}
|
||||
|
||||
function sourceMapListError(err) {
|
||||
hideDots();
|
||||
appElements.fetchingMessage.style.color = "#FF7777";
|
||||
appElements.fetchingMessage.innerText = "Failed to load source map list!";
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
function updateDots() {
|
||||
appElements.loadingDots.innerText = ".".repeat(dotsCounter);
|
||||
dotsCounter = (dotsCounter + 1) % 4;
|
||||
}
|
||||
|
||||
function extractVersionFromFile(lines) {
|
||||
for(var i = 0; i < lines.length; ++i) {
|
||||
var s = lines[i].trim();
|
||||
if(s.startsWith("eaglercraft.version") || s.startsWith("version")) {
|
||||
var v = s.split("=");
|
||||
if(v.length === 2) {
|
||||
var eq = v[1].trim();
|
||||
if(eq.startsWith("\"")) {
|
||||
eq = eq.substring(1);
|
||||
}
|
||||
if(eq.endsWith("\"")) {
|
||||
eq = eq.substring(0, eq.length - 1);
|
||||
}
|
||||
return eq;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var updateTimeout = -1;
|
||||
var lastValue = "";
|
||||
var hadPaste = false;
|
||||
|
||||
function textAreaInputHandler() {
|
||||
if(updateTimeout !== -1) {
|
||||
clearTimeout(updateTimeout);
|
||||
}
|
||||
setTimeout(() => {
|
||||
const oldValue = lastValue;
|
||||
var newLength = (lastValue = appElements.inputTextArea.value).trim().length;
|
||||
if(oldValue.trim().length == 0) {
|
||||
if(newLength > 0) {
|
||||
if(hadPaste) {
|
||||
hadPaste = false;
|
||||
var vers = extractVersionFromFile(lastValue.split(/\r?\n/g));
|
||||
if(vers !== null) {
|
||||
for(var k of sourceMapURLs.keys()) {
|
||||
if(vers === k) {
|
||||
appElements.sourceMaps.value = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setButton(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(newLength > 0) {
|
||||
if(!hasInput) {
|
||||
appElements.showOriginal.style.display = "inline";
|
||||
appElements.showDecoded.style.display = "inline";
|
||||
hasInput = true;
|
||||
}
|
||||
appElements.showDecoded.classList.remove("toggleShowDisabled");
|
||||
}else {
|
||||
appElements.showDecoded.classList.add("toggleShowDisabled");
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function textAreaPasteHandler() {
|
||||
textAreaInputHandler();
|
||||
hadPaste = true;
|
||||
}
|
||||
|
||||
function clearDecodedView() {
|
||||
var cnt = appElements.outputContent;
|
||||
while(cnt.firstChild) {
|
||||
cnt.removeChild(cnt.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function showInfo(str, err) {
|
||||
clearDecodedView();
|
||||
var span = document.createElement("span");
|
||||
span.appendChild(document.createTextNode(str));
|
||||
span.style.fontSize = "20px";
|
||||
if(err) {
|
||||
span.style.color = "#FF9999";
|
||||
}
|
||||
appElements.outputContent.appendChild(span);
|
||||
}
|
||||
|
||||
function highlightLine(line) {
|
||||
var e = document.createElement("span");
|
||||
e.style.color = "#FFFF77";
|
||||
e.appendChild(document.createTextNode(line));
|
||||
return e;
|
||||
}
|
||||
|
||||
function formatLine(srcMapLine) {
|
||||
if(srcMapLine.line !== null) {
|
||||
if(srcMapLine.name === null) {
|
||||
return (srcMapLine.source === null ? "<unknown>" : srcMapLine.source) + ":" +
|
||||
srcMapLine.line + (srcMapLine.column > 0 ? ":" + srcMapLine.column : "");
|
||||
}else {
|
||||
return "" + srcMapLine.name + " (" + (srcMapLine.source === null ? "<unknown>" :
|
||||
srcMapLine.source) + ":" + srcMapLine.line + ":" + (srcMapLine.column > 0 ? ":" +
|
||||
srcMapLine.column : "") + ")";
|
||||
}
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getIndent(line) {
|
||||
var ret = "";
|
||||
for(var i = 0; i < line.length; ++i) {
|
||||
var c = line.charAt(i);
|
||||
if(c === " " || c === "\t") {
|
||||
ret += c;
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function makeBold(str) {
|
||||
return "<span style=\"font-weight:bold;font-size:19px;\">" + str.replace("<", "<").replace(">", ">") + "</span>";
|
||||
}
|
||||
|
||||
function printVersionWarning(vers) {
|
||||
const v = appElements.sourceMaps.value;
|
||||
if(sourceMapURLs.get(v) === "LOCAL") {
|
||||
return;
|
||||
}
|
||||
var str = null;
|
||||
if(!vers) {
|
||||
str = "WARNING: no game version (eaglercraft.version) is in this crash report!"
|
||||
}else {
|
||||
if(v !== vers) {
|
||||
str = "WARNING: this crash report seems to be from version '" + makeBold(vers) + "', but you have source map version '" + makeBold(v) + "' selected!";
|
||||
}
|
||||
}
|
||||
if(str !== null) {
|
||||
var e = document.createElement("span");
|
||||
e.style.color = "#FF7777";
|
||||
e.style.fontSize = "16px";
|
||||
e.innerHTML = str + "\n\nIf you don't select the correct source map version then the crash report will be incorrect\n\n";
|
||||
appElements.outputContent.appendChild(e);
|
||||
}
|
||||
}
|
||||
|
||||
function updateSource(srcMap) {
|
||||
clearDecodedView();
|
||||
var sourceValue = appElements.inputTextArea.value;
|
||||
var lines = sourceValue.split(/\r?\n/g);
|
||||
var vers = extractVersionFromFile(lines);
|
||||
var hasShownWarning = false;
|
||||
for(var i = 0; i < lines.length; ++i) {
|
||||
var l = lines[i];
|
||||
var split = l.split(":");
|
||||
|
||||
if(split.length > 1) {
|
||||
var firstToken = split[0].toLowerCase();
|
||||
if(firstToken.endsWith("error")) {
|
||||
if(!hasShownWarning) {
|
||||
hasShownWarning = true;
|
||||
printVersionWarning(vers);
|
||||
}
|
||||
appElements.outputContent.appendChild(highlightLine(l + "\n"));
|
||||
continue;
|
||||
}else if(split.length > 2) {
|
||||
var lineTrim = split[split.length - 2].trim();
|
||||
var lineNo = parseInt(lineTrim);
|
||||
var colTrim = split[split.length - 1].trim();
|
||||
var colNo = parseInt(colTrim);
|
||||
if(isNaN(colNo)) {
|
||||
if(colTrim.length > 1) {
|
||||
colNo = parseInt(colTrim.substring(0, colTrim.length - 1));
|
||||
}
|
||||
}
|
||||
if(!isNaN(lineNo) && !isNaN(colNo)) {
|
||||
var original = formatLine(srcMap.originalPositionFor({ line: lineNo, column: colNo }));
|
||||
if(original !== null) {
|
||||
if(firstToken.endsWith("line")) {
|
||||
appElements.outputContent.appendChild(document.createTextNode(lines[i] + " "));
|
||||
appElements.outputContent.appendChild(highlightLine(original + "\n"));
|
||||
}else {
|
||||
if(!hasShownWarning) {
|
||||
hasShownWarning = true;
|
||||
printVersionWarning(vers);
|
||||
}
|
||||
var idt = getIndent(split[0]);
|
||||
var realStart = split[0].substring(idt.length);
|
||||
if(realStart.startsWith("at")) {
|
||||
appElements.outputContent.appendChild(highlightLine(idt + "at " + original + "\n"));
|
||||
}else {
|
||||
appElements.outputContent.appendChild(highlightLine(idt + original + "\n"));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appElements.outputContent.appendChild(document.createTextNode(lines[i] + "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
function tryUpdateSource(srcMap) {
|
||||
sourceMap.SourceMapConsumer.with(srcMap, null, (srcMapObject) => {
|
||||
try {
|
||||
updateSource(srcMapObject);
|
||||
}catch(e) {
|
||||
isUpdating = false;
|
||||
showInfo("Parse error, check devtools", true);
|
||||
console.error(e);
|
||||
}
|
||||
}).catch((e) => {
|
||||
isUpdating = false;
|
||||
showInfo("Source map load error, check devtools", true);
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
var isUpdating = false;
|
||||
|
||||
function updateDecodedPane() {
|
||||
if(!isUpdating) {
|
||||
const v = appElements.sourceMaps.value;
|
||||
if(v !== "_custom") {
|
||||
var mapJSON = sourceMapLoaded.get(v);
|
||||
|
||||
if(mapJSON) {
|
||||
try {
|
||||
showInfo("Processing source...");
|
||||
tryUpdateSource(mapJSON);
|
||||
return;
|
||||
}catch(e) {
|
||||
isUpdating = false;
|
||||
console.error(e);
|
||||
showInfo("Internal error, check devtools", true);
|
||||
}
|
||||
}
|
||||
|
||||
var mapURL = sourceMapURLs.get(v);
|
||||
|
||||
if(!mapURL) {
|
||||
showInfo("Unknown source map: " + v, true);
|
||||
isUpdating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
showInfo("Loading source map " + v + "...");
|
||||
|
||||
fetch(mapURL)
|
||||
.then((r) => r.json()).then((j) => {
|
||||
sourceMapLoaded.set(v, j);
|
||||
try {
|
||||
showInfo("Processing source...");
|
||||
tryUpdateSource(j);
|
||||
}catch(e) {
|
||||
isUpdating = false;
|
||||
showInfo("Internal error, check devtools", true);
|
||||
}
|
||||
}).catch((e) => {
|
||||
isUpdating = false;
|
||||
showInfo("Could not load source map: " + v, true);
|
||||
});
|
||||
}else {
|
||||
clearDecodedView();
|
||||
showFileChooser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setButton(decodedOrOriginal) {
|
||||
if(decodedOrOriginal) {
|
||||
if(isShowingOriginal) {
|
||||
isShowingOriginal = false;
|
||||
appElements.showDecoded.classList.add("toggleSelected");
|
||||
appElements.showOriginal.classList.remove("toggleSelected");
|
||||
appElements.inputTextArea.style.display = "none";
|
||||
appElements.outputTextArea.style.display = "block";
|
||||
updateDecodedPane();
|
||||
}
|
||||
}else {
|
||||
if(!isShowingOriginal) {
|
||||
isShowingOriginal = true;
|
||||
appElements.showDecoded.classList.remove("toggleSelected");
|
||||
appElements.showOriginal.classList.add("toggleSelected");
|
||||
appElements.inputTextArea.style.display = "block";
|
||||
appElements.outputTextArea.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function comboBoxChangeHandler() {
|
||||
if(!isShowingOriginal) {
|
||||
updateDecodedPane();
|
||||
}
|
||||
}
|
||||
|
||||
function showFileChooser() {
|
||||
const fileChooserElement = document.createElement("input");
|
||||
|
||||
fileChooserElement.type = "file";
|
||||
fileChooserElement.accept = ".json,.map";
|
||||
|
||||
fileChooserElement.addEventListener("change", () => {
|
||||
const files = fileChooserElement.files;
|
||||
if(files.length > 0) {
|
||||
var phile = files[0];
|
||||
var name = phile.name.trim();
|
||||
if(name.endsWith(".json")) {
|
||||
name = name.substring(0, name.length - 5).trim();
|
||||
}
|
||||
if(name.length > 0) {
|
||||
if(name === "_custom") {
|
||||
name = "__custom";
|
||||
}
|
||||
var name2 = name;
|
||||
var i = 0;
|
||||
while(sourceMapURLs.has(name2)) {
|
||||
name2 = name + " (" + (++i) + ")";
|
||||
}
|
||||
const namec = name2;
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener("load", () => {
|
||||
try {
|
||||
var jsonObj = JSON.parse(reader.result);
|
||||
if(jsonObj) {
|
||||
sourceMapURLs.set(namec, "LOCAL");
|
||||
sourceMapLoaded.set(namec, jsonObj);
|
||||
var newOption = document.createElement("option");
|
||||
newOption.value = namec;
|
||||
newOption.appendChild(document.createTextNode(namec));
|
||||
const selector = appElements.sourceMaps;
|
||||
if(selector.childNodes.length > 1) {
|
||||
selector.insertBefore(newOption, selector.childNodes[1]);
|
||||
}else {
|
||||
selector.appendChild(newOption);
|
||||
}
|
||||
selector.value = namec;
|
||||
if(!isShowingOriginal) {
|
||||
updateDecodedPane();
|
||||
}
|
||||
}
|
||||
}catch(e) {
|
||||
console.log(e);
|
||||
alert("Not a valid JSON source map!");
|
||||
}
|
||||
});
|
||||
reader.readAsText(phile);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fileChooserElement.click();
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
|
||||
appElements = {
|
||||
showOriginal: document.getElementById("showOriginal"),
|
||||
showDecoded: document.getElementById("showDecoded"),
|
||||
sourceMaps: document.getElementById("sourceMaps"),
|
||||
inputTextArea: document.getElementById("inputTextArea"),
|
||||
outputTextArea: document.getElementById("outputTextArea"),
|
||||
outputContent: document.getElementById("outputContent"),
|
||||
fetchingMessage: document.getElementById("fetchingMessage"),
|
||||
loadingDots: document.getElementById("loadingDots")
|
||||
};
|
||||
|
||||
dotsInterval = setInterval(updateDots, 300);
|
||||
|
||||
appElements.showOriginal.addEventListener("click", () => setButton(false));
|
||||
|
||||
appElements.showDecoded.addEventListener("click", () => {
|
||||
if(appElements.inputTextArea.value.trim().length > 0) {
|
||||
setButton(true);
|
||||
}
|
||||
});
|
||||
|
||||
appElements.inputTextArea.addEventListener("propertychange", textAreaInputHandler);
|
||||
appElements.inputTextArea.addEventListener("change", textAreaInputHandler);
|
||||
appElements.inputTextArea.addEventListener("click", textAreaInputHandler);
|
||||
appElements.inputTextArea.addEventListener("keyup", textAreaInputHandler);
|
||||
appElements.inputTextArea.addEventListener("input", textAreaInputHandler);
|
||||
appElements.inputTextArea.addEventListener("paste", textAreaPasteHandler);
|
||||
|
||||
appElements.sourceMaps.addEventListener("change", comboBoxChangeHandler);
|
||||
|
||||
fetch("sourceMaps.json")
|
||||
.then((r) => r.json())
|
||||
.then(sourceMapListLoaded)
|
||||
.catch(sourceMapListError);
|
||||
|
||||
});
|
BIN
app/favicon.ico
Normal file
BIN
app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
40
app/index.html
Normal file
40
app/index.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content='reads a "crash report" and deobfuscates javascript stack traces within it using a source map' />
|
||||
<meta name="keywords" content="calder, young, lax1dude, eagler" />
|
||||
<meta name="author" content="lax1dude" />
|
||||
<meta property="og:title" content="Crash Report Viewer" />
|
||||
<meta property="og:locale" content="en-US" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="og_image.png" />
|
||||
<meta property="og:description" content='reads a "crash report" and deobfuscates javascript stack traces within it using a source map' />
|
||||
<title>Crash Report Viewer</title>
|
||||
<link rel="stylesheet" type="text/css" href="crashviewer.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<script type="text/javascript" src="source-map.js"></script>
|
||||
<script type="text/javascript" src="crashviewer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="titleRow">Crash Report Viewer</h1>
|
||||
<div class="containerHead">
|
||||
<p style="margin:0px;">
|
||||
|
||||
<span style="font-size:22;font-weight:bold;margin-right:12px;">Version:</span>
|
||||
<select id="sourceMaps" disabled>
|
||||
<option value="xxx">  </option>
|
||||
</select>
|
||||
<span id="fetchingMessage">Fetching source maps, please wait<span id="loadingDots"></span></span>
|
||||
<span id="showOriginal" class="toggleShow toggleSelected" style="display:none;">Show Original</span>
|
||||
<span id="showDecoded" class="toggleShow toggleShowDisabled" style="display:none;">Show Decoded</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="containerBody">
|
||||
<textarea id="inputTextArea" spellcheck="false" class="darkModeScrollbar" style="display:none;"></textarea>
|
||||
<div id="outputTextArea" style="display:none;">
|
||||
<div id="outputContent" class="darkModeScrollbar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
app/mappings.wasm
Normal file
BIN
app/mappings.wasm
Normal file
Binary file not shown.
BIN
app/og_image.png
Normal file
BIN
app/og_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 886 B |
3351
app/source-map.js
Normal file
3351
app/source-map.js
Normal file
File diff suppressed because it is too large
Load Diff
7
app/sourceMaps.json
Normal file
7
app/sourceMaps.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"sourceMaps": [
|
||||
{ "name": "22w41b", "url": "22w41b.json" },
|
||||
{ "name": "22w41a", "url": "22w41a.json" },
|
||||
{ "name": "22w40a", "url": "22w40a.json" }
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user