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 @@