diff options
Diffstat (limited to 'src/SMAPI.Web/Views/LogParser')
-rw-r--r-- | src/SMAPI.Web/Views/LogParser/Index.cshtml | 674 |
1 files changed, 6 insertions, 668 deletions
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 417fe428..021293b6 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -1,6 +1,12 @@ @{ ViewData["Title"] = "SMAPI log parser"; } +@section Head { + <link rel="stylesheet" href="~/Content/css/log-parser.css" /> + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js" crossorigin="anonymous"></script> + <script src="~/Content/js/log-parser.js"></script> +} <h2>How to share a SMAPI log</h2> <ol> @@ -14,387 +20,10 @@ <em>TODO</em> - <!DOCTYPE html> <html> <head> <title>SMAPI log parser</title> - <style type="text/css"> - body { - font-size: 10pt; - background: #fff; - } - - .mod-repeat { - font-size: 8pt; - } - - input[type="button"] { - cursor: pointer; - } - - .template { - display: none; - } - - .popup, #uploader { - position: fixed; - top: 0px; - left: 0px; - right: 0px; - bottom: 0; - background-color: rgba(0, 0, 0, .33); - z-index: 2; - display: none; - padding: 5px; - } - - #uploader:after { - content: attr(data-text); - display: block; - width: 100px; - height: 24px; - line-height: 25px; - border: 1px solid #000; - background: #fff; - position: absolute; - top: 50%; - left: 50%; - margin: -12px -50px 0 0; - font-size: 18px; - font-weight: bold; - text-align: center; - border-radius: 5px; - } - - .popup h1 { - position: absolute; - top: 10%; - left: 50%; - margin-left: -150px; - text-align: center; - width: 300px; - border: 1px solid #008; - border-radius: 5px; - background: #fff; - font-family: sans-serif; - font-size: 40px; - margin-top: -25px; - } - - .frame { - margin: auto; - margin-top: 25px; - position: absolute; - top: 10%; - left: 10%; - right: 10%; - bottom: 10%; - padding-bottom: 30px; - } - - .buttons { - position: absolute; - display: inline-block; - bottom: -10px; - right: 0px; - padding: 5px; - border: 1px solid #008; - border-radius: 5px; - background: #fff; - } - - #cancel, #closeraw { - font-size: 20px; - border-radius: 5px; - border: 1px solid #880000; - background-color: #fcc; - outline: none; - box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 0, .2); - } - - #cancel:hover, #closeraw:hover { - background-color: #fee; - } - - #submit { - font-size: 20px; - border-radius: 5px; - border: 1px solid #008800; - background-color: #cfc; - outline: none; - box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 0, .2); - } - - #submit:hover { - background-color: #efe; - } - - #input, #dataraw { - width: 100%; - height: 100%; - max-width: 100%; - max-height: 100%; - margin: auto; - box-sizing: border-box; - border-radius: 5px; - border: 1px solid #000088; - outline: none; - box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 192, .2); - } - - .color-red { - color: red; - } - - .color-green { - color: green; - } - - #tabs { - top: 0px; - left: 0px; - right: 0px; - border-bottom: 0; - display: block; - position: fixed; - margin: 0; - padding: 0; - background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(210, 235, 249, 1) 100%); - } - - #tabs li { - margin: 5px 1px 0 0; - height: 25px; - float: left; - display: inline-block; - width: 75px; - border: 1px solid #000000; - border-bottom: 0; - border-radius: 5px 5px 0 0; - text-align: center; - font-family: monospace; - font-size: 18px; - cursor: pointer; - font-weight: bold; - color: #000; - text-shadow: 0px 0px 2px #fff; - border-color: #880000; - background-color: #fcc; - box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 0, .2); - } - - #tabs li:hover { - background-color: #fee; - } - - #tabs li:first-child { - margin-left: 5px; - } - - #tabs li.active { - background: #cfc; - border-color: #008800; - } - - #tabs li.active:hover { - background: #efe; - } - - #tabs li.upload { - float: right; - background: #ccf; - border-color: #000088; - margin-right: 5px; - } - - #tabs li.upload:hover { - background: #eef; - } - - #tabs li.notice { - color: #000000; - background: transparent; - border: 0; - padding-top: 1px; - font-size: 13px; - font-weight: normal; - width: auto; - margin-left: 5px; - cursor: default; - box-shadow: none; - font-style: italic; - } - - #output { - border-top: 1px solid #888; - position: fixed; - top: 30px; - left: 0px; - right: 0px; - bottom: 0px; - padding: 10px; - overflow: auto; - font-family: monospace; - } - - #output > * { - display: block; - } - - #output.trace .trace, - #output.debug .debug, - #output.info .info, - #output.alert .alert, - #output.warn .warn, - #output.error .error { - display: none; - } - - #output .trace { - color: #999; - } - - #output .debug { - color: #595959; - } - - #output .info { - color: #000 - } - - #output .alert { - color: #b0b; - } - - #output .warn { - color: #f80 - } - - #output .error { - color: #f00 - } - - #output .always { - font-weight: bold; - border-bottom: 1px dashed #888888; - padding-bottom: 10px; - margin-bottom: 5px; - } - - caption { - text-align: left; - padding-top: 2px; - } - - #log { - border-spacing: 0; - } - - #log tr { - background: #fff; - } - - #log td { - padding: 0 1px; - background: inherit; - border-bottom: 1px dotted #ccc; - border-top: 2px solid #fff; - vertical-align: top; - } - - #log td:not(:last-child) { - max-width: 175px; - padding: 0 4px; - overflow: hidden; - white-space: nowrap; - } - - #log td[data-title]:hover { - font-size: 1px; - overflow: inherit; - position: relative; - } - - #log td:nth-child(3):hover:after { - content: attr(data-title); - display: block; - position: absolute; - border-radius: 4px; - box-shadow: 1px 1px 2px #ccc; - background: inherit; - border: 1px solid #ccc; - background: #efefef; - padding: 1px 1px 0 1px; - font-size: 10pt; - top: -2px; - left: 2px; - color: #000; - } - - #log td:last-child { - width: 100%; - } - - table#gameinfo, - table#modslist { - border: 1px solid #000000; - background: #ffffff; - border-radius: 5px; - border-spacing: 1px; - overflow: hidden; - cursor: default; - box-shadow: 1px 1px 1px 1px #dddddd; - } - - #modslist { - min-width: 400px; - } - - #gameinfo td:first-child { - padding-right: 5px; - } - - #gameinfo tr, - #modslist tr { - background: #eee - } - - #gameinfo tr:nth-child(even), - #modslist tr:nth-child(even) { - background: #fff - } - - #modslist tr { - cursor: pointer; - } - - span.notice { - font-weight: normal; - font-size: 11px; - position: relative; - top: -1px; - display: none; - } - - span.notice.btn { - cursor: pointer; - border: 1px solid #000; - border-radius: 5px; - position: relative; - top: -1px; - padding: 0 2px; - background: #eee; - } - - #output:not(.modfilter) span.notice.txt { - display: inline-block; - } - - #output.modfilter span.notice.btn { - display: inline-block; - } - </style> <style type="text/css" id="modflags"></style> </head> <body> @@ -486,296 +115,5 @@ </div> </div> <div id="uploader"></div> - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js" crossorigin="anonymous"></script> - <script> - $(function() { - function modClicked(evt) { - var id = $(evt.currentTarget).attr("id").split('-')[1], - cls = "mod-" + id;; - if (output.hasClass(cls)) - filters--; - else - filters++; - output.toggleClass(cls); - if (filters == 0) { - output.removeClass("modfilter"); - } else { - output.addClass("modfilter"); - } - } - - function removeFilter() { - for (var c = 0; c < modInfo.length; c++) { - output.removeClass("mod-" + c); - } - filters = 0; - output.removeClass("modfilter"); - } - - function selectAll() { - for (var c = 0; c < modInfo.length; c++) { - output.addClass("mod-" + c); - } - filters = modInfo.length; - output.addClass("modfilter"); - } - - function parseData() { - Stage = "parseData.pre"; - var data = $("#input").val(); - if (!data) { - Stage = "parseData.checkNullData"; - throw new Error("Field `data` is null"); - - } - var dataInfo = regexInfo.exec(data) || regexInfo.exec(data) || regexInfo.exec(data), - dataMods = regexMods.exec(data) || regexMods.exec(data) || regexMods.exec(data), - dataDate = regexDate.exec(data) || regexDate.exec(data) || regexDate.exec(data), - dataPath = regexPath.exec(data) || regexPath.exec(data) || regexPath.exec(data), - match; - console.log("dataInfo:", dataInfo); - console.log("dataMods:", dataMods); - console.log("dataDate:", dataDate); - console.log("dataPath:", dataPath); - Stage = "parseData.doNullCheck"; - if (!dataInfo) - throw new Error("Field `dataInfo` is null"); - if (!dataMods) - throw new Error("Field `dataMods` is null"); - if (!dataPath) - throw new Error("Field `dataPath` is null"); - dataMods = dataMods[0]; - Stage = "parseData.setupDefaults"; - modMap = { - "SMAPI": 0 - }; - modErrors = { - "SMAPI": 0, - "Console.Out": 0 - }; - logInfo = []; - modInfo = [ - ["SMAPI", dataInfo[1], "Zoryn, CLxS & Pathoschild"] - ]; - Stage = "parseData.parseInfo"; - if (dataDate) - var date = new Date(dataDate[1] + "Z"); - versionInfo = [dataInfo[1], dataInfo[2], dataInfo[3], dataDate ? date.getFullYear() + "-" + ("0" + date.getMonth().toString()).substr(-2) + "-" + ("0" + date.getDay().toString()).substr(-2) + " at " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + " " + date.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2] : "No timestamp found", dataPath[1]]; - Stage = "parseData.parseMods"; - while (match = regexMod.exec(dataMods)) { - modErrors[match[1]] = 0; - modMap[match[1]] = modInfo.length; - modInfo.push([match[1], match[2], match[3] ? ("by " + match[3]) : "Unknown author"]); - } - Stage = "parseData.parseLog"; - while (match = regexLog.exec(data)) { - if (match[2] == "ERROR") - modErrors[match[3]]++; - logInfo.push([match[1], match[2], match[3], match[4]]); - } - Stage = "parseData.post"; - modMap["Console.Out"] = modInfo.length; - modInfo.push(["Console.Out", "", ""]); - } - - function renderData() { - Stage = "renderData.pre"; - output.html(prepare(templateBody, versionInfo)); - var modslist = $("#modslist"), log = $("#log"), modCache = [], y = 0 - for (; y < modInfo.length; y++) { - var errors = modErrors[modInfo[y][0]], - err, cls = "color-red"; - if (errors == 0) { - err = "No Errors"; - cls = "color-green"; - } else if (errors == 1) - err = "1 Error"; - else - err = errors + " Errors"; - modCache.push(prepare(templateModentry, [y, modInfo[y][0], modInfo[y][1], modInfo[y][2], cls, err])); - } - modslist.append(modCache.join("")); - for (var z = 0; z < modInfo.length; z++) - $("#modlink-" + z).on("click", modClicked); - var flagCache = []; - for (var c = 0; c < modInfo.length; c++) - flagCache.push(prepare(templateCss, [c])); - flags.html(flagCache.join("")); - var logCache = [], dupeCount = 0, dupeMemory = "|||"; - for (var x = 0; x < logInfo.length; x++) { - var dm = logInfo[x][1] + "|" + logInfo[x][2] + "|" + logInfo[x][3]; - if (dupeMemory != dm) { - if (dupeCount > 0) - logCache.push(prepare(templateLognotice, [logInfo[x - 1][1].toLowerCase(), modMap[logInfo[x - 1][2]], dupeCount])); - dupeCount = 0; - dupeMemory = dm; - logCache.push(prepare(templateLogentry, [logInfo[x][1].toLowerCase(), modMap[logInfo[x][2]], logInfo[x][0], logInfo[x][1], logInfo[x][2], logInfo[x][3].split(" ").join("  ").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br />")])); - } - else - dupeCount++; - } - log.append(logCache.join("")); - $("#modlink-r").on("click", removeFilter); - $("#modlink-a").on("click", selectAll); - } - - function prepare(str, arr) { - var regex = /\{(\d)\}/g, - match; - while (match = regex.exec(str)) - str = str.replace(match[0], arr[match[1]]); - return str; - } - function loadData() { - try { - Stage = "loadData.Pre"; - var start = performance.now(); - parseData(); - renderData(); - var end = performance.now(); - $(".always").prepend('<div>Log processed in: ' + (Math.round((end - start) * 100) / 100) + ' ms (<a id="viewraw" href="#">View raw</a>)</div><br />'); - $("#viewraw").on("click", function() { - $("#dataraw").val($("#input").val()); - $("#popup-raw").fadeIn(); - }) - Stage = "loadData.Post"; - } - catch (err) { - $("#output").html('<div id="log" class="color-red"><h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: ' + Stage + '</p>' + err + '<hr /><div id="rawlog"></div></div>'); - $("#rawlog").text($("#input").val()); - } - } - function getData() { - $("#uploader").attr("data-text", "Loading..."); - $("#uploader").fadeIn(); - $.get("https://cors-anywhere.herokuapp.com/pastebin.com/raw/" + location.search.substring(1) + "/?nocache=" + Math.random(), function(data, state, xhr) { - if (data.substring(0, 9) == "<!DOCTYPE") { - $("#output").html('<div id="log" class="color-red"><h1>Captcha required!</h1>The pastebin server is asking for a captcha, but their API doesnt let us show it to you directly.<br />Instead, to finish saving the log, you need to <a href="https://pastebin.com/' + location.search.substring(1) + '" target="_blank">solve the captcha in a new tab</a>, once you have done so, reload this page.</div>'); - } - else { - $("#input").val(LZString.decompressFromUTF16(data) || data); - loadData(); - } - $("#uploader").fadeOut(); - }); - } - var Stage, - flags = $("#modflags"), - output = $("#output"), - filters = 0, - memory = "", - versionInfo, - modInfo, - modMap, - modErrors, - logInfo, - templateBody = $("#template-body").text(), - templateModentry = $("#template-modentry").text(), - templateCss = $("#template-css").text(), - templateLogentry = $("#template-logentry").text(), - templateLognotice = $("#template-lognotice").text(), - regexInfo = /\[[\d\:]+ INFO SMAPI] SMAPI (.*?) with Stardew Valley (.*?) on (.*?)\n/g, - regexMods = /\[[^\]]+\] Loaded \d+ mods:(?:\n\[[^\]]+\] .+)+/g, - regexLog = /\[([\d\:]+) (TRACE|DEBUG|INFO|WARN|ALERT|ERROR) ? ([^\]]+)\] ?((?:\n|.)*?)(?=(?:\[\d\d:|$))/g, - regexMod = /\[(?:.*?)\] *(.*?) (\d+\.?(?:.*?))(?: by (.*?))? \|(?:.*?)$/gm, - regexDate = /\[\d{2}:\d{2}:\d{2} TRACE SMAPI\] Log started at (.*?) UTC/g, - regexPath = /\[\d{2}:\d{2}:\d{2} DEBUG SMAPI\] Mods go here: (.*?)(?:\n|$)/g - ; - $("#tabs li:not(.notice)").on("click", function(evt) { - var t = $(evt.currentTarget) - t.toggleClass("active"); - $("#output").toggleClass(t.text().toLowerCase()); - }) - $("#upload").on("click", function() { - memory = $("#input").val() || ""; - $("#input").val(""); - $("#popup-upload").fadeIn(); - }) - var proxies = [ - "https://cors-anywhere.herokuapp.com/", - "https://galvanize-cors-proxy.herokuapp.com/" - ]; - $('#popup-upload').on({ - 'dragover dragenter': function(e) { - e.preventDefault(); - e.stopPropagation(); - }, - 'drop': function(e) { - $("#uploader").attr("data-text", "Reading...") - $("#uploader").show(); - var dataTransfer = e.originalEvent.dataTransfer; - if (dataTransfer && dataTransfer.files.length) { - e.preventDefault(); - e.stopPropagation(); - var file = dataTransfer.files[0]; - var reader = new FileReader(); - reader.onload = $.proxy(function(file, $input, event) { - $input.val(event.target.result); - $("#uploader").fadeOut(); - $("#submit").click(); - }, this, file, $("#input")); - reader.readAsText(file); - } - } - }); - function logSize(id, str) { - console.log(id + ":", str.length * 2, "bytes", Math.round(str.length / 5.12) / 100, "kb"); - } - $("#submit").on("click", function() { - $("#popup-upload").fadeOut(); - if ($("#input").val()) { - memory = ""; - var raw = $("#input").val(); - var paste = LZString.compressToUTF16(raw); - logSize("Raw", raw); - logSize("Compressed", paste); - if (paste.length * 2 > 524288) { - $("#output").html('<div id="log" class="color-red"><h1>Unable to save!</h1>This log cannot be saved due to its size.<hr />' + $("#input").val() + '</div>'); - return; - } - console.log("paste:", paste); - var packet = { - api_dev_key: "b8219d942109d1e60ebb14fbb45f06f9", - api_option: "paste", - api_paste_private: 1, - api_paste_code: paste, - api_paste_expire_date: "1W" - }; - $("#uploader").attr("data-text", "Saving..."); - $("#uploader").fadeIn(); - var uri = proxies[Math.floor(Math.random() * proxies.length)] + "pastebin.com/api/api_post.php"; - console.log(packet, uri); - $.post(uri, packet, function(data, state, xhr) { - $("#uploader").fadeOut(); - console.log("Result: ", data); - if (data.substring(0, 15) == "Bad API request") - $("#output").html('<div id="log" class="color-red"><h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: Upload</p>Error: ' + data + '<hr />' + $("#input").val() + '</div>'); - else if (data) - location.href = "?" + data.split('/').pop(); - else - $("#output").html('<div id="log" class="color-red"><h1>Parsing failed!</h1>Parsing of the log failed, details follow.<br /> <p>Stage: Upload</p>Error: Received null response<hr />' + $("#input").val() + '</div>'); - }) - } else { - alert("Unable to parse log, the input is empty!"); - $("#uploader").fadeOut(); - } - }) - $("#cancel").on("click", function() { - $("#popup-upload").fadeOut(400, function() { - $("#input").val(memory); - memory = ""; - }); - }); - $("#closeraw").on("click", function() { - $("#popup-raw").fadeOut(400); - }) - if (location.search) { - getData(); - } - else - $("#popup-upload").fadeIn(); - }) - </script> </body> </html> |