add file pickers to web UI for mobile users
This commit is contained in:
parent
5ae640dc91
commit
a6b1103596
|
@ -1,6 +1,10 @@
|
|||
← [README](README.md)
|
||||
|
||||
# Release notes
|
||||
## Upcoming release
|
||||
* For the web UI:
|
||||
* Added option to upload files using a file picker.
|
||||
|
||||
## 3.3.2
|
||||
Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
||||
|
||||
|
@ -27,6 +31,10 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
|||
* Fixed warning on MacOS when you have no saves yet.
|
||||
* Reduced log messages.
|
||||
|
||||
* For the web UI:
|
||||
* Updated the JSON validator and Content Patcher schema for `.tmx` support.
|
||||
* The mod compatibility page now has a sticky table header.
|
||||
|
||||
* For modders:
|
||||
* Added support for [message sending](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Message_sending) to mods on the current computer (in addition to remote computers).
|
||||
* Added `ExtendImage` method to content API when editing files to resize textures.
|
||||
|
@ -37,10 +45,6 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later.
|
|||
* Updated dependencies (including Mono.Cecil 0.11.1 → 0.11.2).
|
||||
* Fixed dialogue propagation clearing marriage dialogue.
|
||||
|
||||
* For the web UI:
|
||||
* Updated the JSON validator and Content Patcher schema for `.tmx` support.
|
||||
* The mod compatibility page now has a sticky table header.
|
||||
|
||||
* For SMAPI/tool developers:
|
||||
* Improved support for four-part versions to support SMAPI on Android.
|
||||
* The SMAPI log now prefixes the OS name with `Android` on Android.
|
||||
|
|
|
@ -28,14 +28,16 @@
|
|||
{
|
||||
<meta name="robots" content="noindex" />
|
||||
}
|
||||
<link rel="stylesheet" href="~/Content/css/json-validator.css?r=20191204" />
|
||||
<link rel="stylesheet" href="~/Content/css/file-upload.css?r=202002" />
|
||||
<link rel="stylesheet" href="~/Content/css/json-validator.css?r=202002" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/themes/sunlight.default.min.css" />
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/sunlight.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/plugins/sunlight-plugin.linenumbers.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/lang/sunlight.javascript.min.js" crossorigin="anonymous"></script>
|
||||
<script src="~/Content/js/json-validator.js?r=20191204"></script>
|
||||
<script src="~/Content/js/file-upload.js?r=202002"></script>
|
||||
<script src="~/Content/js/json-validator.js?r=202002"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
smapi.jsonValidator(@Json.Serialize(this.Url.PlainAction("Index", "JsonValidator", new { schemaName = "$schemaName", id = "$id" })), @Json.Serialize(Model.PasteID));
|
||||
|
@ -86,6 +88,7 @@ else if (!isEditView && Model.PasteID != null)
|
|||
{
|
||||
<h2>Upload a JSON file</h2>
|
||||
<form action="@this.Url.PlainAction("PostAsync", "JsonValidator")" method="post">
|
||||
<input id="inputFile" type="file" />
|
||||
<ol>
|
||||
<li>
|
||||
Choose the JSON format:<br />
|
||||
|
@ -97,7 +100,7 @@ else if (!isEditView && Model.PasteID != null)
|
|||
</select>
|
||||
</li>
|
||||
<li>
|
||||
Drag the file onto this textbox (or paste the text in):<br />
|
||||
Drag the file onto this textbox <small>(or <a href="#" id="choose-file-link">choose a file</a>)</small>:<br />
|
||||
<textarea id="input" name="Content" placeholder="paste file here">@Model.Content</textarea>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -22,10 +22,13 @@
|
|||
{
|
||||
<meta name="robots" content="noindex" />
|
||||
}
|
||||
<link rel="stylesheet" href="~/Content/css/log-parser.css?r=20191127" />
|
||||
<link rel="stylesheet" href="~/Content/css/file-upload.css?r=202002" />
|
||||
<link rel="stylesheet" href="~/Content/css/log-parser.css?r=202002" />
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
||||
<script src="~/Content/js/log-parser.js?r=20190515"></script>
|
||||
<script src="~/Content/js/file-upload.js?r=202002"></script>
|
||||
<script src="~/Content/js/log-parser.js?r=202002"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
smapi.logParser({
|
||||
|
@ -135,9 +138,10 @@ else if (Model.ParsedLog?.IsValid == true)
|
|||
|
||||
<h2>How do I share my log?</h2>
|
||||
<form action="@this.Url.PlainAction("PostAsync", "LogParser")" method="post">
|
||||
<input id="inputFile" type="file" />
|
||||
<ol>
|
||||
<li>
|
||||
Drag the file onto this textbox (or paste the text in):<br />
|
||||
Drag the file onto this textbox <small>(or <a href="#" id="choose-file-link">choose a file</a>)</small>:<br />
|
||||
<textarea id="input" name="input" placeholder="paste log here"></textarea>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -300,7 +304,7 @@ else if (Model.ParsedLog?.IsValid == true)
|
|||
string sectionFilter = message.Section != null && !message.IsStartOfSection ? $"&& sectionsAllow('{message.Section}')" : null; // filter the message by section if applicable
|
||||
|
||||
<tr class="mod @levelStr @sectionStartClass"
|
||||
@if (message.IsStartOfSection) { <text> v-on:click="toggleSection('@message.Section')" </text> }
|
||||
@if (message.IsStartOfSection) { <text> v-on:click="toggleSection('@message.Section')" </text> }
|
||||
v-show="filtersAllow('@Model.GetSlug(message.Mod)', '@levelStr') @sectionFilter">
|
||||
<td v-pre>@message.Time</td>
|
||||
<td v-pre>@message.Level.ToString().ToUpper()</td>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#inputFile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#input {
|
||||
width: 100%;
|
||||
height: 20em;
|
||||
max-height: 70%;
|
||||
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);
|
||||
}
|
||||
|
||||
#submit {
|
||||
font-size: 1.5em;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, .2);
|
||||
cursor: pointer;
|
||||
border: 1px solid #008800;
|
||||
background-color: #cfc;
|
||||
}
|
|
@ -90,28 +90,3 @@
|
|||
.footer-tip a {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
/*********
|
||||
** Upload form
|
||||
*********/
|
||||
#input {
|
||||
width: 100%;
|
||||
height: 20em;
|
||||
max-height: 70%;
|
||||
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);
|
||||
}
|
||||
|
||||
#submit {
|
||||
font-size: 1.5em;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, .2);
|
||||
cursor: pointer;
|
||||
border: 1px solid #008800;
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
|
|
@ -301,24 +301,3 @@ div[data-os] {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#input {
|
||||
width: 100%;
|
||||
height: 20em;
|
||||
max-height: 70%;
|
||||
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);
|
||||
}
|
||||
|
||||
#submit {
|
||||
font-size: 1.5em;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, .2);
|
||||
cursor: pointer;
|
||||
border: 1px solid #008800;
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* globals $ */
|
||||
var smapi = smapi || {};
|
||||
|
||||
/**
|
||||
* Implements the logic for a log/JSON file upload form.
|
||||
*
|
||||
* @param {object} opts The file upload form options.
|
||||
* @param {jQuery} opts.chooseFileLink The clickable link which shows the file chooser.
|
||||
* @param {jQuery} opts.chooseFileInput The file input element.
|
||||
* @param {jQuery} opts.contentArea The file content area.
|
||||
* @param {jQuery} opts.submitButton The submit button.
|
||||
*/
|
||||
smapi.fileUpload = function (opts) {
|
||||
/**
|
||||
* Toggle the submit button if the form has content.
|
||||
*/
|
||||
var toggleSubmit = function () {
|
||||
var hasText = !!opts.contentArea.val().trim();
|
||||
opts.submitButton.prop("disabled", !hasText);
|
||||
};
|
||||
|
||||
/**
|
||||
* Paste the content of a file into the content area.
|
||||
* @param {File} file The file whose content to paste.
|
||||
*/
|
||||
var pasteFile = function (file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = $.proxy(function (file, $input, event) {
|
||||
$input.val(event.target.result);
|
||||
toggleSubmit();
|
||||
}, this, file, $("#input"));
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
// initialize
|
||||
if (opts.contentArea.length) {
|
||||
// disable submit button if it's empty
|
||||
opts.contentArea.on("input", toggleSubmit);
|
||||
toggleSubmit();
|
||||
|
||||
// drag & drop file
|
||||
opts.contentArea.on({
|
||||
"dragover dragenter": function (e) {
|
||||
e.preventDefault();
|
||||
},
|
||||
"drop": function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var transfer = e.originalEvent.dataTransfer;
|
||||
if (transfer && transfer.files.length)
|
||||
pasteFile(transfer.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
// choose file
|
||||
opts.chooseFileLink.on({
|
||||
"click": function (e) {
|
||||
e.preventDefault();
|
||||
opts.chooseFileInput.click();
|
||||
}
|
||||
});
|
||||
opts.chooseFileInput.on({
|
||||
"change": function (e) {
|
||||
if (!e.target.files || !e.target.files.length)
|
||||
return;
|
||||
|
||||
pasteFile(e.target.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
|
@ -41,7 +41,7 @@ smapi.LineNumberRange = function (maxLines) {
|
|||
* Generate a URL hash for the current line range.
|
||||
* @returns {string} The generated URL hash.
|
||||
*/
|
||||
self.buildHash = function() {
|
||||
self.buildHash = function () {
|
||||
if (!self.minLine)
|
||||
return "";
|
||||
else if (self.minLine === self.maxLine)
|
||||
|
@ -54,7 +54,7 @@ smapi.LineNumberRange = function (maxLines) {
|
|||
* Get a list of all selected lines.
|
||||
* @returns {Array<int>} The selected line numbers.
|
||||
*/
|
||||
self.getLinesSelected = function() {
|
||||
self.getLinesSelected = function () {
|
||||
// format
|
||||
if (!self.minLine)
|
||||
return [];
|
||||
|
@ -97,7 +97,7 @@ smapi.jsonValidator = function (urlFormat, fileId) {
|
|||
});
|
||||
|
||||
// fix line links
|
||||
$(".sunlight-line-number-margin a").each(function() {
|
||||
$(".sunlight-line-number-margin a").each(function () {
|
||||
var link = $(this);
|
||||
var lineNumber = parseInt(link.text());
|
||||
link
|
||||
|
@ -111,7 +111,7 @@ smapi.jsonValidator = function (urlFormat, fileId) {
|
|||
/**
|
||||
* Scroll the page so the selected range is visible.
|
||||
*/
|
||||
var scrollToRange = function() {
|
||||
var scrollToRange = function () {
|
||||
if (!selection.minLine)
|
||||
return;
|
||||
|
||||
|
@ -123,56 +123,33 @@ smapi.jsonValidator = function (urlFormat, fileId) {
|
|||
* Initialize the JSON validator page.
|
||||
*/
|
||||
var init = function () {
|
||||
var input = $("#input");
|
||||
|
||||
// set initial code formatting
|
||||
selection.parseFromUrlHash(location.hash);
|
||||
formatCode();
|
||||
scrollToRange();
|
||||
|
||||
// update code formatting on hash change
|
||||
$(window).on("hashchange", function() {
|
||||
$(window).on("hashchange", function () {
|
||||
selection.parseFromUrlHash(location.hash);
|
||||
formatCode();
|
||||
scrollToRange();
|
||||
});
|
||||
|
||||
// change format
|
||||
$("#output #format").on("change", function() {
|
||||
$("#output #format").on("change", function () {
|
||||
var schemaName = $(this).val();
|
||||
location.href = urlFormat.replace("$schemaName", schemaName).replace("$id", fileId);
|
||||
});
|
||||
|
||||
// upload form
|
||||
var submit = $("#submit");
|
||||
var input = $("#input");
|
||||
if (input.length) {
|
||||
// disable submit if it's empty
|
||||
var toggleSubmit = function () {
|
||||
var hasText = !!input.val().trim();
|
||||
submit.prop("disabled", !hasText);
|
||||
};
|
||||
input.on("input", toggleSubmit);
|
||||
toggleSubmit();
|
||||
|
||||
// drag & drop file
|
||||
input.on({
|
||||
'dragover dragenter': function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
'drop': function (e) {
|
||||
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);
|
||||
toggleSubmit();
|
||||
}, this, file, $("#input"));
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
// upload form
|
||||
smapi.fileUpload({
|
||||
chooseFileLink: $("#choose-file-link"),
|
||||
chooseFileInput: $("#inputFile"),
|
||||
contentArea: input,
|
||||
submitButton: $("#submit")
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -115,12 +115,10 @@ smapi.logParser = function (data, sectionUrl) {
|
|||
*********/
|
||||
var input = $("#input");
|
||||
if (input.length) {
|
||||
// get elements
|
||||
// instructions per OS
|
||||
var systemOptions = $("input[name='os']");
|
||||
var systemInstructions = $("div[data-os]");
|
||||
var submit = $("#submit");
|
||||
|
||||
// instruction OS chooser
|
||||
var chooseSystem = function () {
|
||||
systemInstructions.hide();
|
||||
systemInstructions.filter("[data-os='" + $("input[name='os']:checked").val() + "']").show();
|
||||
|
@ -128,34 +126,12 @@ smapi.logParser = function (data, sectionUrl) {
|
|||
systemOptions.on("click", chooseSystem);
|
||||
chooseSystem();
|
||||
|
||||
// disable submit if it's empty
|
||||
var toggleSubmit = function () {
|
||||
var hasText = !!input.val().trim();
|
||||
submit.prop("disabled", !hasText);
|
||||
}
|
||||
input.on("input", toggleSubmit);
|
||||
toggleSubmit();
|
||||
|
||||
// drag & drop file
|
||||
input.on({
|
||||
'dragover dragenter': function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
'drop': function (e) {
|
||||
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);
|
||||
toggleSubmit();
|
||||
}, this, file, $("#input"));
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
// file upload
|
||||
smapi.fileUpload({
|
||||
chooseFileLink: $("#choose-file-link"),
|
||||
chooseFileInput: $("#inputFile"),
|
||||
contentArea: input,
|
||||
submitButton: $("#submit")
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue