From af71ca6a25bfb8f1d4e449d84bdfaa677e3603d8 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 26 Dec 2017 00:44:40 +0000 Subject: [PATCH] Output over 1MiB is passed back as an ArrayBuffer and an output file card is displayed. --- src/core/Chef.js | 7 ++- src/core/Utils.js | 25 +++++++- src/web/InputWaiter.js | 9 +-- src/web/OutputWaiter.js | 92 +++++++++++++++++++++--------- src/web/WorkerWaiter.js | 14 ++++- src/web/html/index.html | 15 ++++- src/web/stylesheets/layout/_io.css | 3 +- 7 files changed, 130 insertions(+), 35 deletions(-) diff --git a/src/core/Chef.js b/src/core/Chef.js index d176a36d..06d08aa0 100755 --- a/src/core/Chef.js +++ b/src/core/Chef.js @@ -76,10 +76,15 @@ Chef.prototype.bake = async function(input, recipeConfig, options, progress, ste progress = err.progress; } + // Depending on the size of the output, we may send it back as a string or an ArrayBuffer. + // This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file. + // 1048576 bytes = 1 MiB + const returnType = this.dish.size() > 1048576 ? Dish.ARRAY_BUFFER : Dish.STRING; + return { result: this.dish.type === Dish.HTML ? this.dish.get(Dish.HTML) : - this.dish.get(Dish.STRING), + this.dish.get(returnType), type: Dish.enumLookup(this.dish.type), progress: progress, duration: new Date().getTime() - startTime, diff --git a/src/core/Utils.js b/src/core/Utils.js index a4a455a2..4ab365c8 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -349,7 +349,14 @@ const Utils = { * @returns {byteArray} * * @example - * // returns [] + * // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130] + * Utils.convertToByteArray("Привет", "utf8"); + * + * // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130] + * Utils.convertToByteArray("d097d0b4d180d0b0d0b2d181d182d0b2d183d0b9d182d0b5", "hex"); + * + * // returns [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130] + * Utils.convertToByteArray("0JfQtNGA0LDQstGB0YLQstGD0LnRgtC1", "base64"); */ convertToByteArray: function(str, type) { switch (type.toLowerCase()) { @@ -511,6 +518,22 @@ const Utils = { }, + /** + * Converts an ArrayBuffer to a string. + * + * @param {ArrayBuffer} arrayBuffer + * @returns {string} + * + * @example + * // returns "hello" + * Utils.arrayBufferToStr(Uint8Array.from([104,101,108,108,111]).buffer); + */ + arrayBufferToStr: function(arrayBuffer) { + const byteArray = Array.prototype.slice.call(new Uint8Array(arrayBuffer)); + return Utils.byteArrayToUtf8(byteArray); + }, + + /** * Base64's the input byte array using the given alphabet, returning a string. * diff --git a/src/web/InputWaiter.js b/src/web/InputWaiter.js index b5748e3f..e5a58fe1 100755 --- a/src/web/InputWaiter.js +++ b/src/web/InputWaiter.js @@ -79,13 +79,13 @@ InputWaiter.prototype.setFile = function(file) { fileName = document.getElementById("input-file-name"), fileSize = document.getElementById("input-file-size"), fileType = document.getElementById("input-file-type"), - fileUploaded = document.getElementById("input-file-uploaded"); + fileLoaded = document.getElementById("input-file-loaded"); fileOverlay.style.display = "block"; fileName.textContent = file.name; fileSize.textContent = file.size.toLocaleString() + " bytes"; fileType.textContent = file.type; - fileUploaded.textContent = "0%"; + fileLoaded.textContent = "0%"; }; @@ -210,8 +210,8 @@ InputWaiter.prototype.inputDrop = function(e) { InputWaiter.prototype.handleLoaderMessage = function(e) { const r = e.data; if (r.hasOwnProperty("progress")) { - const fileUploaded = document.getElementById("input-file-uploaded"); - fileUploaded.textContent = r.progress + "%"; + const fileLoaded = document.getElementById("input-file-loaded"); + fileLoaded.textContent = r.progress + "%"; } if (r.hasOwnProperty("fileBuffer")) { @@ -246,6 +246,7 @@ InputWaiter.prototype.clearIoClick = function() { document.getElementById("output-info").innerHTML = ""; document.getElementById("input-selection-info").innerHTML = ""; document.getElementById("output-selection-info").innerHTML = ""; + document.getElementById("output-file").style.display = "none"; window.dispatchEvent(this.manager.statechange); }; diff --git a/src/web/OutputWaiter.js b/src/web/OutputWaiter.js index 076d62b2..1c857074 100755 --- a/src/web/OutputWaiter.js +++ b/src/web/OutputWaiter.js @@ -31,47 +31,85 @@ OutputWaiter.prototype.get = function() { /** * Sets the output in the output textarea. * - * @param {string} dataStr - The output string/HTML + * @param {string|ArrayBuffer} data - The output string/HTML/ArrayBuffer * @param {string} type - The data type of the output * @param {number} duration - The length of time (ms) it took to generate the output */ -OutputWaiter.prototype.set = function(dataStr, type, duration) { +OutputWaiter.prototype.set = function(data, type, duration) { const outputText = document.getElementById("output-text"); const outputHtml = document.getElementById("output-html"); + const outputFile = document.getElementById("output-file"); const outputHighlighter = document.getElementById("output-highlighter"); const inputHighlighter = document.getElementById("input-highlighter"); + let scriptElements, lines, length; - if (type === "html") { - outputText.style.display = "none"; - outputHtml.style.display = "block"; - outputHighlighter.display = "none"; - inputHighlighter.display = "none"; + switch (type) { + case "html": + outputText.style.display = "none"; + outputHtml.style.display = "block"; + outputFile.style.display = "none"; + outputHighlighter.display = "none"; + inputHighlighter.display = "none"; - outputText.value = ""; - outputHtml.innerHTML = dataStr; + outputText.value = ""; + outputHtml.innerHTML = data; + length = data.length; - // Execute script sections - const scriptElements = outputHtml.querySelectorAll("script"); - for (let i = 0; i < scriptElements.length; i++) { - try { - eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval - } catch (err) { - console.error(err); + // Execute script sections + scriptElements = outputHtml.querySelectorAll("script"); + for (let i = 0; i < scriptElements.length; i++) { + try { + eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval + } catch (err) { + console.error(err); + } } - } - } else { - outputText.style.display = "block"; - outputHtml.style.display = "none"; - outputHighlighter.display = "block"; - inputHighlighter.display = "block"; + break; + case "ArrayBuffer": + outputText.style.display = "block"; + outputHtml.style.display = "none"; + outputHighlighter.display = "none"; + inputHighlighter.display = "none"; - outputText.value = Utils.printable(dataStr, true); - outputHtml.innerHTML = ""; + outputText.value = ""; + outputHtml.innerHTML = ""; + length = data.byteLength; + + this.setFile(new File([data], "output.dat")); + break; + case "string": + default: + outputText.style.display = "block"; + outputHtml.style.display = "none"; + outputFile.style.display = "none"; + outputHighlighter.display = "block"; + inputHighlighter.display = "block"; + + outputText.value = Utils.printable(data, true); + outputHtml.innerHTML = ""; + + lines = data.count("\n") + 1; + length = data.length; + break; } this.manager.highlighter.removeHighlights(); - const lines = dataStr.count("\n") + 1; - this.setOutputInfo(dataStr.length, lines, duration); + this.setOutputInfo(length, lines, duration); +}; + + +/** + * Shows file details. + * + * @param {File} file + */ +OutputWaiter.prototype.setFile = function(file) { + // Display file overlay in output area with details + const fileOverlay = document.getElementById("output-file"), + fileSize = document.getElementById("output-file-size"); + + fileOverlay.style.display = "block"; + fileSize.textContent = file.size.toLocaleString() + " bytes"; }; @@ -86,6 +124,8 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) { let width = length.toString().length; width = width < 4 ? 4 : width; + lines = typeof lines === "number" ? lines : ""; + const lengthStr = Utils.pad(length.toString(), width, " ").replace(/ /g, " "); const linesStr = Utils.pad(lines.toString(), width, " ").replace(/ /g, " "); const timeStr = Utils.pad(duration.toString() + "ms", width, " ").replace(/ /g, " "); diff --git a/src/web/WorkerWaiter.js b/src/web/WorkerWaiter.js index 6fc69e40..3ed9e7ea 100644 --- a/src/web/WorkerWaiter.js +++ b/src/web/WorkerWaiter.js @@ -111,7 +111,19 @@ WorkerWaiter.prototype.bakingComplete = function(response) { this.app.handleError(response.error); } - this.app.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result; + switch (response.type) { + case "html": + this.app.dishStr = Utils.stripHtmlTags(response.result, true); + break; + case "ArrayBuffer": + this.app.dishStr = ""; + break; + case "string": + default: + this.app.dishStr = response.result; + break; + } + this.app.progress = response.progress; this.manager.recipe.updateBreakpointIndicator(response.progress); this.manager.output.set(response.result, response.type, response.duration); diff --git a/src/web/html/index.html b/src/web/html/index.html index 3bfefa8b..dd6260ea 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -190,7 +190,7 @@ Name:
Size:
Type:
- Uploaded: + Loaded: @@ -216,6 +216,19 @@
+
+
+
+ +
+ Size:
+ Download
+ Display in output
+ Options for how much to display +
+
+
+
diff --git a/src/web/stylesheets/layout/_io.css b/src/web/stylesheets/layout/_io.css index 855d4262..519b81fc 100644 --- a/src/web/stylesheets/layout/_io.css +++ b/src/web/stylesheets/layout/_io.css @@ -77,7 +77,8 @@ transition: all 0.5s ease; } -#input-file { +#input-file, +#output-file { position: absolute; left: 0; top: 0;