From 65f0fa625575592639a24a9b39330e4a6b500f22 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:36:31 -0400 Subject: add scaffolding for web UI (#358) --- src/SMAPI.Web/Controllers/ModsApiController.cs | 162 +++++++++++++++++++++++++ src/SMAPI.Web/Controllers/ModsController.cs | 162 ------------------------- src/SMAPI.Web/Startup.cs | 1 + src/SMAPI.Web/Views/Shared/_Layout.cshtml | 29 +++++ src/SMAPI.Web/Views/_ViewStart.cshtml | 3 + src/SMAPI.Web/wwwroot/Content/main.css | 107 ++++++++++++++++ src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif | Bin 0 -> 1104 bytes src/SMAPI.Web/wwwroot/favicon.ico | Bin 0 -> 15086 bytes 8 files changed, 302 insertions(+), 162 deletions(-) create mode 100644 src/SMAPI.Web/Controllers/ModsApiController.cs delete mode 100644 src/SMAPI.Web/Controllers/ModsController.cs create mode 100644 src/SMAPI.Web/Views/Shared/_Layout.cshtml create mode 100644 src/SMAPI.Web/Views/_ViewStart.cshtml create mode 100644 src/SMAPI.Web/wwwroot/Content/main.css create mode 100644 src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif create mode 100644 src/SMAPI.Web/wwwroot/favicon.ico (limited to 'src') diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs new file mode 100644 index 00000000..1db5b59e --- /dev/null +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using StardewModdingAPI.Common.Models; +using StardewModdingAPI.Web.Framework.ConfigModels; +using StardewModdingAPI.Web.Framework.ModRepositories; + +namespace StardewModdingAPI.Web.Controllers +{ + /// Provides an API to perform mod update checks. + [Produces("application/json")] + [Route("api/{version:semanticVersion}/mods")] + internal class ModsApiController : Controller + { + /********* + ** Properties + *********/ + /// The mod repositories which provide mod metadata. + private readonly IDictionary Repositories; + + /// The cache in which to store mod metadata. + private readonly IMemoryCache Cache; + + /// The number of minutes update checks should be cached before refetching them. + private readonly int CacheMinutes; + + /// A regex which matches SMAPI-style semantic version. + private readonly string VersionRegex; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The cache in which to store mod metadata. + /// The config settings for mod update checks. + public ModsApiController(IMemoryCache cache, IOptions configProvider) + { + ModUpdateCheckConfig config = configProvider.Value; + + this.Cache = cache; + this.CacheMinutes = config.CacheMinutes; + this.VersionRegex = config.SemanticVersionRegex; + + string version = this.GetType().Assembly.GetName().Version.ToString(3); + this.Repositories = + new IModRepository[] + { + new ChucklefishRepository( + vendorKey: config.ChucklefishKey, + userAgent: string.Format(config.ChucklefishUserAgent, version), + baseUrl: config.ChucklefishBaseUrl, + modPageUrlFormat: config.ChucklefishModPageUrlFormat + ), + new GitHubRepository( + vendorKey: config.GitHubKey, + baseUrl: config.GitHubBaseUrl, + releaseUrlFormat: config.GitHubReleaseUrlFormat, + userAgent: string.Format(config.GitHubUserAgent, version), + acceptHeader: config.GitHubAcceptHeader, + username: config.GitHubUsername, + password: config.GitHubPassword + ), + new NexusRepository( + vendorKey: config.NexusKey, + userAgent: config.NexusUserAgent, + baseUrl: config.NexusBaseUrl, + modUrlFormat: config.NexusModUrlFormat + ) + } + .ToDictionary(p => p.VendorKey, StringComparer.CurrentCultureIgnoreCase); + } + + /// Fetch version metadata for the given mods. + /// The namespaced mod keys to search as a comma-delimited array. + [HttpGet] + public async Task> GetAsync(string modKeys) + { + string[] modKeysArray = modKeys?.Split(',').ToArray(); + if (modKeysArray == null || !modKeysArray.Any()) + return new Dictionary(); + + return await this.PostAsync(new ModSearchModel(modKeysArray)); + } + + /// Fetch version metadata for the given mods. + /// The mod search criteria. + [HttpPost] + public async Task> PostAsync([FromBody] ModSearchModel search) + { + // sort & filter keys + string[] modKeys = (search?.ModKeys?.ToArray() ?? new string[0]) + .Distinct(StringComparer.CurrentCultureIgnoreCase) + .OrderBy(p => p, StringComparer.CurrentCultureIgnoreCase) + .ToArray(); + + // fetch mod info + IDictionary result = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + foreach (string modKey in modKeys) + { + // parse mod key + if (!this.TryParseModKey(modKey, out string vendorKey, out string modID)) + { + result[modKey] = new ModInfoModel("The mod key isn't in a valid format. It should contain the site key and mod ID like 'Nexus:541'."); + continue; + } + + // get matching repository + if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository)) + { + result[modKey] = new ModInfoModel($"There's no mod site with key '{vendorKey}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}]."); + continue; + } + + // fetch mod info + result[modKey] = await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{modID}".ToLower(), async entry => + { + entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes); + + ModInfoModel info = await repository.GetModInfoAsync(modID); + if (info.Error == null && (info.Version == null || !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))) + info = new ModInfoModel(info.Name, info.Version, info.Url, info.Version == null ? "Mod has no version number." : $"Mod has invalid semantic version '{info.Version}'."); + + return info; + }); + } + + return result; + } + + + /********* + ** Private methods + *********/ + /// Parse a namespaced mod ID. + /// The raw mod ID to parse. + /// The parsed vendor key. + /// The parsed mod ID. + /// Returns whether the value could be parsed. + private bool TryParseModKey(string raw, out string vendorKey, out string modID) + { + // split parts + string[] parts = raw?.Split(':'); + if (parts == null || parts.Length != 2) + { + vendorKey = null; + modID = null; + return false; + } + + // parse + vendorKey = parts[0].Trim(); + modID = parts[1].Trim(); + return true; + } + } +} diff --git a/src/SMAPI.Web/Controllers/ModsController.cs b/src/SMAPI.Web/Controllers/ModsController.cs deleted file mode 100644 index a671ddca..00000000 --- a/src/SMAPI.Web/Controllers/ModsController.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Options; -using StardewModdingAPI.Common.Models; -using StardewModdingAPI.Web.Framework.ConfigModels; -using StardewModdingAPI.Web.Framework.ModRepositories; - -namespace StardewModdingAPI.Web.Controllers -{ - /// Provides an API to perform mod update checks. - [Produces("application/json")] - [Route("api/{version:semanticVersion}/[controller]")] - internal class ModsController : Controller - { - /********* - ** Properties - *********/ - /// The mod repositories which provide mod metadata. - private readonly IDictionary Repositories; - - /// The cache in which to store mod metadata. - private readonly IMemoryCache Cache; - - /// The number of minutes update checks should be cached before refetching them. - private readonly int CacheMinutes; - - /// A regex which matches SMAPI-style semantic version. - private readonly string VersionRegex; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The cache in which to store mod metadata. - /// The config settings for mod update checks. - public ModsController(IMemoryCache cache, IOptions configProvider) - { - ModUpdateCheckConfig config = configProvider.Value; - - this.Cache = cache; - this.CacheMinutes = config.CacheMinutes; - this.VersionRegex = config.SemanticVersionRegex; - - string version = this.GetType().Assembly.GetName().Version.ToString(3); - this.Repositories = - new IModRepository[] - { - new ChucklefishRepository( - vendorKey: config.ChucklefishKey, - userAgent: string.Format(config.ChucklefishUserAgent, version), - baseUrl: config.ChucklefishBaseUrl, - modPageUrlFormat: config.ChucklefishModPageUrlFormat - ), - new GitHubRepository( - vendorKey: config.GitHubKey, - baseUrl: config.GitHubBaseUrl, - releaseUrlFormat: config.GitHubReleaseUrlFormat, - userAgent: string.Format(config.GitHubUserAgent, version), - acceptHeader: config.GitHubAcceptHeader, - username: config.GitHubUsername, - password: config.GitHubPassword - ), - new NexusRepository( - vendorKey: config.NexusKey, - userAgent: config.NexusUserAgent, - baseUrl: config.NexusBaseUrl, - modUrlFormat: config.NexusModUrlFormat - ) - } - .ToDictionary(p => p.VendorKey, StringComparer.CurrentCultureIgnoreCase); - } - - /// Fetch version metadata for the given mods. - /// The namespaced mod keys to search as a comma-delimited array. - [HttpGet] - public async Task> GetAsync(string modKeys) - { - string[] modKeysArray = modKeys?.Split(',').ToArray(); - if (modKeysArray == null || !modKeysArray.Any()) - return new Dictionary(); - - return await this.PostAsync(new ModSearchModel(modKeysArray)); - } - - /// Fetch version metadata for the given mods. - /// The mod search criteria. - [HttpPost] - public async Task> PostAsync([FromBody] ModSearchModel search) - { - // sort & filter keys - string[] modKeys = (search?.ModKeys?.ToArray() ?? new string[0]) - .Distinct(StringComparer.CurrentCultureIgnoreCase) - .OrderBy(p => p, StringComparer.CurrentCultureIgnoreCase) - .ToArray(); - - // fetch mod info - IDictionary result = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - foreach (string modKey in modKeys) - { - // parse mod key - if (!this.TryParseModKey(modKey, out string vendorKey, out string modID)) - { - result[modKey] = new ModInfoModel("The mod key isn't in a valid format. It should contain the site key and mod ID like 'Nexus:541'."); - continue; - } - - // get matching repository - if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository)) - { - result[modKey] = new ModInfoModel($"There's no mod site with key '{vendorKey}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}]."); - continue; - } - - // fetch mod info - result[modKey] = await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{modID}".ToLower(), async entry => - { - entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes); - - ModInfoModel info = await repository.GetModInfoAsync(modID); - if (info.Error == null && (info.Version == null || !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))) - info = new ModInfoModel(info.Name, info.Version, info.Url, info.Version == null ? "Mod has no version number." : $"Mod has invalid semantic version '{info.Version}'."); - - return info; - }); - } - - return result; - } - - - /********* - ** Private methods - *********/ - /// Parse a namespaced mod ID. - /// The raw mod ID to parse. - /// The parsed vendor key. - /// The parsed mod ID. - /// Returns whether the value could be parsed. - private bool TryParseModKey(string raw, out string vendorKey, out string modID) - { - // split parts - string[] parts = raw?.Split(':'); - if (parts == null || parts.Length != 2) - { - vendorKey = null; - modID = null; - return false; - } - - // parse - vendorKey = parts[0].Trim(); - modID = parts[1].Trim(); - return true; - } - } -} diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index eaf14983..abce8f28 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -64,6 +64,7 @@ namespace StardewModdingAPI.Web loggerFactory.AddDebug(); app .UseRewriter(new RewriteOptions().Add(new RewriteSubdomainRule())) // convert subdomain.smapi.io => smapi.io/subdomain for routing + .UseStaticFiles() // wwwroot folder .UseMvc(); } } diff --git a/src/SMAPI.Web/Views/Shared/_Layout.cshtml b/src/SMAPI.Web/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..89b1866c --- /dev/null +++ b/src/SMAPI.Web/Views/Shared/_Layout.cshtml @@ -0,0 +1,29 @@ + + + + + @ViewData["Title"] - SMAPI.io + + + + +
+
+

@ViewData["Title"]

+ @RenderBody() +
+ +
+ + diff --git a/src/SMAPI.Web/Views/_ViewStart.cshtml b/src/SMAPI.Web/Views/_ViewStart.cshtml new file mode 100644 index 00000000..a5f10045 --- /dev/null +++ b/src/SMAPI.Web/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/src/SMAPI.Web/wwwroot/Content/main.css b/src/SMAPI.Web/wwwroot/Content/main.css new file mode 100644 index 00000000..c8ce8d33 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/main.css @@ -0,0 +1,107 @@ +/* tags */ +html { + height: 100%; +} + +body { + height: 100%; + font-family: sans-serif; +} + +h1, h2, h3 { + font-weight: bold; + margin: 0.2em 0 0.1em 0; + padding-top: .5em; +} + +h1 { + font-size: 1.5em; + color: #888; + margin-bottom: 0; +} + +h2 { + font-size: 1.5em; + border-bottom: 1px solid #AAA; +} + +h3 { + font-size: 1.2em; + border-bottom: 1px solid #AAA; + width: 50%; +} + +a { + color: #006; +} + +/* content */ +#content-column { + position: absolute; + top: 1em; + left: 10em; +} + +#content { + min-height: 140px; + padding: 0 1em 1em 1em; + border-left: 1px solid #CCC; + background: #FFF; + font-size: 0.9em; +} + +#content p { + max-width: 55em; +} + +.section { + border: 1px solid #CCC; + padding: 0.5em; + margin-bottom: 1em; +} + +/* sidebar */ +#sidebar { + margin-top: 3em; + min-height: 75%; + width: 12em; + background: url("sidebar-bg.gif") no-repeat top right; + color: #666; +} + +#sidebar h4 { + margin: 0 0 0.2em 0; + width: 10em; + border-bottom: 1px solid #CCC; + font-size: 0.8em; + font-weight: normal; +} + +#sidebar a { + color: #77B; + border: 0; +} + +#sidebar ul, #sidebar li { + margin: 0; + padding: 0; + list-style: none none; + font-size: 0.9em; + color: #888; +} + +#sidebar li { + margin-left: 1em; +} + +/* footer */ +#footer { + margin: 1em; + padding: 1em; + font-size: 0.6em; + color: gray; +} + +#footer a { + color: #669; +} diff --git a/src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif b/src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif new file mode 100644 index 00000000..48e9af5a Binary files /dev/null and b/src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif differ diff --git a/src/SMAPI.Web/wwwroot/favicon.ico b/src/SMAPI.Web/wwwroot/favicon.ico new file mode 100644 index 00000000..587a6e74 Binary files /dev/null and b/src/SMAPI.Web/wwwroot/favicon.ico differ -- cgit From e75aef8634f9edb8ac385b8d7308b40ed3269cbc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:36:52 -0400 Subject: add placeholder for new log parser (#358) --- src/SMAPI.Web/Controllers/LogParserController.cs | 19 +++++++++++++++++++ src/SMAPI.Web/Views/LogParser/Index.cshtml | 14 ++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/SMAPI.Web/Controllers/LogParserController.cs create mode 100644 src/SMAPI.Web/Views/LogParser/Index.cshtml (limited to 'src') diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs new file mode 100644 index 00000000..4ed8898a --- /dev/null +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Mvc; + +namespace StardewModdingAPI.Web.Controllers +{ + /// Provides a web UI and API for parsing SMAPI log files. + [Route("log")] + internal class LogParserController : Controller + { + /********* + ** Public methods + *********/ + /// Render the web UI to upload a log file. + [HttpGet] + public ViewResult Index() + { + return this.View("Index"); + } + } +} diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml new file mode 100644 index 00000000..cd47d687 --- /dev/null +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -0,0 +1,14 @@ +@{ + ViewData["Title"] = "SMAPI log parser"; +} + +

How to share a SMAPI log

+
    +
  1. Find your SMAPI log.
  2. +
  3. Click the file and drag it onto the upload form below (or paste the text in).
  4. +
  5. Click Parse.
  6. +
  7. Share the link you get back.
  8. +
+ +

Parsed log

+TODO -- cgit From a26220e3410aa7f0a043c1bcd0ab845e210c1bbc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:37:26 -0400 Subject: add log parser prototype by Entoarox (#358) --- src/SMAPI.Web/Views/LogParser/Index.cshtml | 769 +++++++++++++++++++++++++++++ 1 file changed, 769 insertions(+) (limited to 'src') diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index cd47d687..830cfe47 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -12,3 +12,772 @@

Parsed log

TODO + + + + + + + SMAPI log parser + + + + +
    +
  • TRACE
  • +
  • DEBUG
  • +
  • INFO
  • +
  • ALERT
  • +
  • WARN
  • +
  • ERROR
  • +
  • Click tabs to toggle message visibility
  • +
  • UPLOAD
  • +
+
+ + + + + + + +
+ + + + + -- cgit From 6cbe43a233eccbc6c8d1cfdd9c80e391463eb7c8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:37:49 -0400 Subject: use CDN for jQuery and lz-string (#358) --- src/SMAPI.Web/Views/LogParser/Index.cshtml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 830cfe47..417fe428 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -486,9 +486,8 @@
- + + - -- cgit From 9f5af37391ac196fe183122f57496846843335cd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:38:13 -0400 Subject: move log parser CSS/JS out of HTML (#358) --- src/SMAPI.Web/Views/LogParser/Index.cshtml | 674 +-------------------- src/SMAPI.Web/Views/Shared/_Layout.cshtml | 3 +- src/SMAPI.Web/wwwroot/Content/css/log-parser.css | 374 ++++++++++++ src/SMAPI.Web/wwwroot/Content/css/main.css | 107 ++++ .../wwwroot/Content/images/sidebar-bg.gif | Bin 0 -> 1104 bytes src/SMAPI.Web/wwwroot/Content/js/log-parser.js | 287 +++++++++ src/SMAPI.Web/wwwroot/Content/main.css | 107 ---- src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif | Bin 1104 -> 0 bytes 8 files changed, 776 insertions(+), 776 deletions(-) create mode 100644 src/SMAPI.Web/wwwroot/Content/css/log-parser.css create mode 100644 src/SMAPI.Web/wwwroot/Content/css/main.css create mode 100644 src/SMAPI.Web/wwwroot/Content/images/sidebar-bg.gif create mode 100644 src/SMAPI.Web/wwwroot/Content/js/log-parser.js delete mode 100644 src/SMAPI.Web/wwwroot/Content/main.css delete mode 100644 src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif (limited to 'src') 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 { + + + + +}

How to share a SMAPI log

    @@ -14,387 +20,10 @@ TODO - SMAPI log parser - @@ -486,296 +115,5 @@
    - - - diff --git a/src/SMAPI.Web/Views/Shared/_Layout.cshtml b/src/SMAPI.Web/Views/Shared/_Layout.cshtml index 89b1866c..547a8178 100644 --- a/src/SMAPI.Web/Views/Shared/_Layout.cshtml +++ b/src/SMAPI.Web/Views/Shared/_Layout.cshtml @@ -3,7 +3,8 @@ @ViewData["Title"] - SMAPI.io - + + @RenderSection("Head", required: false) '); + } + 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('

    Unable to save!

    This log cannot be saved due to its size.
    ' + $("#input").val() + '
    '); + 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('

    Parsing failed!

    Parsing of the log failed, details follow.
     

    Stage: Upload

    Error: ' + data + '
    ' + $("#input").val() + '
    '); + else if (data) + location.href = "?" + data.split('/').pop(); + else + $("#output").html('

    Parsing failed!

    Parsing of the log failed, details follow.
     

    Stage: Upload

    Error: Received null response
    ' + $("#input").val() + '
    '); + }) + } 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(); +}) diff --git a/src/SMAPI.Web/wwwroot/Content/main.css b/src/SMAPI.Web/wwwroot/Content/main.css deleted file mode 100644 index c8ce8d33..00000000 --- a/src/SMAPI.Web/wwwroot/Content/main.css +++ /dev/null @@ -1,107 +0,0 @@ -/* tags */ -html { - height: 100%; -} - -body { - height: 100%; - font-family: sans-serif; -} - -h1, h2, h3 { - font-weight: bold; - margin: 0.2em 0 0.1em 0; - padding-top: .5em; -} - -h1 { - font-size: 1.5em; - color: #888; - margin-bottom: 0; -} - -h2 { - font-size: 1.5em; - border-bottom: 1px solid #AAA; -} - -h3 { - font-size: 1.2em; - border-bottom: 1px solid #AAA; - width: 50%; -} - -a { - color: #006; -} - -/* content */ -#content-column { - position: absolute; - top: 1em; - left: 10em; -} - -#content { - min-height: 140px; - padding: 0 1em 1em 1em; - border-left: 1px solid #CCC; - background: #FFF; - font-size: 0.9em; -} - -#content p { - max-width: 55em; -} - -.section { - border: 1px solid #CCC; - padding: 0.5em; - margin-bottom: 1em; -} - -/* sidebar */ -#sidebar { - margin-top: 3em; - min-height: 75%; - width: 12em; - background: url("sidebar-bg.gif") no-repeat top right; - color: #666; -} - -#sidebar h4 { - margin: 0 0 0.2em 0; - width: 10em; - border-bottom: 1px solid #CCC; - font-size: 0.8em; - font-weight: normal; -} - -#sidebar a { - color: #77B; - border: 0; -} - -#sidebar ul, #sidebar li { - margin: 0; - padding: 0; - list-style: none none; - font-size: 0.9em; - color: #888; -} - -#sidebar li { - margin-left: 1em; -} - -/* footer */ -#footer { - margin: 1em; - padding: 1em; - font-size: 0.6em; - color: gray; -} - -#footer a { - color: #669; -} diff --git a/src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif b/src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif deleted file mode 100644 index 48e9af5a..00000000 Binary files a/src/SMAPI.Web/wwwroot/Content/sidebar-bg.gif and /dev/null differ -- cgit From 467b9aa2df8532aa3cb94c84307c7012573d61d4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:38:37 -0400 Subject: integrate prototype into page layout (#358) --- src/SMAPI.Web/Views/LogParser/Index.cshtml | 197 +++++++++++------------ src/SMAPI.Web/wwwroot/Content/css/log-parser.css | 77 +++------ src/SMAPI.Web/wwwroot/Content/js/log-parser.js | 2 +- 3 files changed, 122 insertions(+), 154 deletions(-) (limited to 'src') diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 021293b6..87a3962b 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -6,114 +6,107 @@ + } -

    How to share a SMAPI log

    -
      -
    1. Find your SMAPI log.
    2. -
    3. Click the file and drag it onto the upload form below (or paste the text in).
    4. -
    5. Click Parse.
    6. -
    7. Share the link you get back.
    8. -
    +@********* +** Intro +*********@ +

    This page lets you upload, view, and share a SMAPI log to help troubleshoot mod issues.

    +

    Parsed log

    -TODO - - - - - SMAPI log parser - - - -
      -
    • TRACE
    • -
    • DEBUG
    • -
    • INFO
    • -
    • ALERT
    • -
    • WARN
    • -
    • ERROR
    • -
    • Click tabs to toggle message visibility
    • -
    • UPLOAD
    • -
    -
    - - - - - -