/* globals $ */
var smapi = smapi || {};
smapi.logParser = function(sectionUrl, pasteID) {
/*********
** Initialisation
*********/
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-button").on("click", function() {
memory = $("#input").val() || "";
$("#input").val("");
$("#popup-upload").fadeIn();
});
$("#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);
}
}
});
$("#submit").on("click", function() {
$("#popup-upload").fadeOut();
var paste = $("#input").val();
if (paste) {
memory = "";
$("#uploader").attr("data-text", "Saving...");
$("#uploader").fadeIn();
$
.ajax({
type: "POST",
url: sectionUrl + "/save",
data: JSON.stringify(paste),
contentType: "application/json" // sent to API
})
.fail(function(xhr, textStatus) {
$("#uploader").fadeOut();
$("#output").html('
Parsing failed!
Parsing of the log failed, details follow.
Stage: Upload
Error: ' + textStatus + ': ' + xhr.responseText + "
" + $("#input").val() + "
");
})
.then(function(data) {
$("#uploader").fadeOut();
if (!data.success)
$("#output").html('Parsing failed!
Parsing of the log failed, details follow.
Stage: Upload
Error: ' + data.error + "
" + $("#input").val() + "
");
else
location.href = (sectionUrl.replace(/\/$/, "") + "/" + data.id);
});
} 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 (pasteID) {
getData(pasteID);
}
else
$("#popup-upload").fadeIn();
/*********
** Helpers
*********/
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;
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";
var date = dataDate ? new Date(dataDate[1] + "Z") : null;
versionInfo = [dataInfo[1], dataInfo[2], dataInfo[3], date ? 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(/\n/g, "
")]));
}
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("Log processed in: " + (Math.round((end - start) * 100) / 100) + ' ms (
View raw)
');
$("#viewraw").on("click", function() {
$("#dataraw").val($("#input").val());
$("#popup-raw").fadeIn();
});
stage = "loadData.Post";
}
catch (err) {
$("#output").html('Parsing failed!
Parsing of the log failed, details follow.
Stage: ' + stage + "
" + err + '
');
$("#rawlog").text($("#input").val());
}
}
function getData(pasteID) {
$("#uploader").attr("data-text", "Loading...");
$("#uploader").fadeIn();
$.get(sectionUrl + "/fetch/" + pasteID, function(data) {
if (data.success) {
$("#input").val(data.content);
loadData();
} else {
$("#output").html('Fetching the log failed!
' + data.error + '
');
$("#rawlog").text($("#input").val());
}
$("#uploader").fadeOut();
});
}
};