diff --git a/src/core/Dish.mjs b/src/core/Dish.mjs index a79d20cf..bd05f4b0 100755 --- a/src/core/Dish.mjs +++ b/src/core/Dish.mjs @@ -144,11 +144,10 @@ class Dish { /** * Detects the MIME type of the current dish - * * @returns {string} */ async detectDishType() { - const data = new Uint8Array(this.value), + const data = new Uint8Array(this.value.slice(0, 2048)), types = detectFileType(data); if (!types.length || !types[0].mime || !types[0].mime === "text/plain") { @@ -168,7 +167,6 @@ class Dish { async getTitle(maxLength) { let title = ""; - // LIST OF FILES - Say e.g. "3 files" switch (this.type) { case Dish.FILE: title = this.value.name; @@ -180,6 +178,7 @@ class Dish { case Dish.BYTE_ARRAY: title = await this.detectDishType(); if (title === null) { + this.value = this.value.slice(0, 2048); title = await this.get("string"); } break; diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index ee5e4553..c64712ac 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -166,7 +166,6 @@ class Manager { this.addListeners("#btn-next-input-tab,#btn-previous-input-tab", "mouseout", this.input.tabMouseUp, this.input); document.getElementById("btn-go-to-input-tab").addEventListener("click", this.input.goToTab.bind(this.input)); document.getElementById("btn-find-input-tab").addEventListener("click", this.input.findTab.bind(this.input)); - this.addDynamicListener("#input-tabs li .input-tab-content", "wheel", this.input.scrollTab, this.input); this.addDynamicListener("#input-tabs li .input-tab-content", "click", this.input.changeTabClick, this.input); document.getElementById("input-show-pending").addEventListener("change", this.input.filterTabSearch.bind(this.input)); document.getElementById("input-show-loading").addEventListener("change", this.input.filterTabSearch.bind(this.input)); @@ -202,7 +201,6 @@ class Manager { document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output)); this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output); this.addDynamicListener("#output-tabs-wrapper #output-tabs li .output-tab-content", "click", this.output.changeTabClick, this.output); - this.addDynamicListener("#output-tabs-wrapper #output-tabs li .output-tab-content", "wheel", this.output.scrollTab, this.output); document.getElementById("btn-previous-output-tab").addEventListener("mousedown", this.output.previousTabClick.bind(this.output)); document.getElementById("btn-next-output-tab").addEventListener("mousedown", this.output.nextTabClick.bind(this.output)); this.addListeners("#btn-next-output-tab,#btn-previous-output-tab", "mouseup", this.output.tabMouseUp, this.output); diff --git a/src/web/waiters/InputWaiter.mjs b/src/web/waiters/InputWaiter.mjs index 199077a2..e9e841c8 100644 --- a/src/web/waiters/InputWaiter.mjs +++ b/src/web/waiters/InputWaiter.mjs @@ -49,9 +49,14 @@ class InputWaiter { this.loaderWorkers = []; this.workerId = 0; this.maxTabs = this.manager.tabs.calcMaxTabs(); - this.maxWorkers = navigator.hardwareConcurrency || 4; this.callbacks = {}; this.callbackID = 0; + + this.maxWorkers = 1; + if (navigator.hardwareConcurrency !== undefined && + navigator.hardwareConcurrency > 1) { + this.maxWorkers = navigator.hardwareConcurrency - 1; + } } /** diff --git a/src/web/waiters/OutputWaiter.mjs b/src/web/waiters/OutputWaiter.mjs index 04279b2f..dca3bde9 100755 --- a/src/web/waiters/OutputWaiter.mjs +++ b/src/web/waiters/OutputWaiter.mjs @@ -269,7 +269,8 @@ class OutputWaiter { this.toggleLoader(true); return new Promise(async function(resolve, reject) { - const output = this.outputs[inputNum]; + const output = this.outputs[inputNum], + activeTab = this.manager.tabs.getActiveOutputTab(); if (output === undefined || output === null) return; if (typeof inputNum !== "number") inputNum = parseInt(inputNum, 10); @@ -370,7 +371,7 @@ class OutputWaiter { outputHtml.innerHTML = ""; length = output.data.result.byteLength; - this.setFile(await this.getDishBuffer(output.data.dish)); + this.setFile(await this.getDishBuffer(output.data.dish), activeTab); break; case "string": default: @@ -405,8 +406,10 @@ class OutputWaiter { * Shows file details * * @param {ArrayBuffer} buf + * @param {number} activeTab */ - setFile(buf) { + setFile(buf, activeTab) { + if (activeTab !== this.manager.tabs.getActiveOutputTab()) return; // Display file overlay in output area with details const fileOverlay = document.getElementById("output-file"), fileSize = document.getElementById("output-file-size"), diff --git a/src/web/waiters/TabWaiter.mjs b/src/web/waiters/TabWaiter.mjs index 2af6948c..fd4689c5 100644 --- a/src/web/waiters/TabWaiter.mjs +++ b/src/web/waiters/TabWaiter.mjs @@ -157,6 +157,8 @@ class TabWaiter { newTabContent.innerText = `Tab ${inputNum.toString()}`; + newTabContent.addEventListener("wheel", this.manager[io].scrollTab.bind(this.manager[io]), {passive: false}); + newTab.appendChild(newTabContent); if (io === "input") { diff --git a/src/web/waiters/WorkerWaiter.mjs b/src/web/waiters/WorkerWaiter.mjs index 1099cab8..82f3df27 100644 --- a/src/web/waiters/WorkerWaiter.mjs +++ b/src/web/waiters/WorkerWaiter.mjs @@ -6,6 +6,7 @@ */ import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker"; +import DishWorker from "worker-loader?inline&fallback=false!../workers/DishWorker"; /** * Waiter to handle conversations with the ChefWorker @@ -24,8 +25,6 @@ class WorkerWaiter { this.loaded = false; this.chefWorkers = []; - this.dishWorker = null; - this.maxWorkers = navigator.hardwareConcurrency || 4; this.inputs = []; this.inputNums = []; this.totalOutputs = 0; @@ -33,6 +32,19 @@ class WorkerWaiter { this.bakeId = 0; this.callbacks = {}; this.callbackID = 0; + + this.maxWorkers = 1; + if (navigator.hardwareConcurrency !== undefined && + navigator.hardwareConcurrency > 1) { + this.maxWorkers = navigator.hardwareConcurrency - 1; + } + + // Store dishWorker action (getDishAs or getDishTitle) + this.dishWorker = { + worker: null, + currentAction: "" + }; + this.dishWorkerQueue = []; } /** @@ -48,30 +60,20 @@ class WorkerWaiter { } /** - * Sets up a separate ChefWorker for performing dish operations. - * Using a separate worker so that we can run dish operations without - * affecting a bake which may be in progress. + * Sets up a DishWorker to be used for performing Dish operations */ setupDishWorker() { - if (this.dishWorker !== null) { - this.dishWorker.terminate(); + if (this.dishWorker.worker !== null) { + this.dishWorker.worker.terminate(); } - log.debug("Adding new ChefWorker (DishWorker)"); + log.debug("Adding new DishWorker"); - this.dishWorker = new ChefWorker(); - this.dishWorker.addEventListener("message", this.handleChefMessage.bind(this)); + this.dishWorker.worker = new DishWorker(); + this.dishWorker.worker.addEventListener("message", this.handleDishMessage.bind(this)); - let docURL = document.location.href.split(/[#?]/)[0]; - const index = docURL.lastIndexOf("/"); - if (index > 0) { - docURL = docURL.substring(0, index); + if (this.dishWorkerQueue.length > 0) { + this.postDishMessage(this.dishWorkerQueue.splice(0, 1)); } - - this.dishWorker.postMessage({"action": "docURL", "data": docURL}); - this.dishWorker.postMessage({ - action: "setLogLevel", - data: log.getLevel() - }); } /** @@ -242,7 +244,6 @@ class WorkerWaiter { } } - /** * Update the value of an output * @@ -607,6 +608,31 @@ class WorkerWaiter { }); } + /** + * Handler for messages sent back from DishWorker + * + * @param {MessageEvent} e + */ + handleDishMessage(e) { + const r = e.data; + log.debug(`Receiving ${r.action} from DishWorker`); + + switch (r.action) { + case "dishReturned": + this.dishWorker.currentAction = ""; + this.callbacks[r.data.id](r.data); + + if (this.dishWorkerQueue.length > 0) { + this.postDishMessage(this.dishWorkerQueue.splice(0, 1)[0]); + } + + break; + default: + log.error("Unrecognised message from DishWorker", e); + break; + } + } + /** * Asks the ChefWorker to return the dish as the specified type * @@ -619,9 +645,9 @@ class WorkerWaiter { this.callbacks[id] = callback; - if (this.dishWorker === null) this.setupDishWorker(); + if (this.dishWorker.worker === null) this.setupDishWorker(); - this.dishWorker.postMessage({ + this.postDishMessage({ action: "getDishAs", data: { dish: dish, @@ -644,9 +670,9 @@ class WorkerWaiter { this.callbacks[id] = callback; - if (this.dishWorker === null) this.setupDishWorker(); + if (this.dishWorker.worker === null) this.setupDishWorker(); - this.dishWorker.postMessage({ + this.postDishMessage({ action: "getDishTitle", data: { dish: dish, @@ -656,6 +682,39 @@ class WorkerWaiter { }); } + /** + * Queues a message to be sent to the dishWorker + * + * @param {object} message + * @param {string} message.action + * @param {object} message.data + * @param {Dish} message.data.dish + * @param {number} message.data.id + */ + queueDishMessage(message) { + if (message.action === "getDishAs") { + this.dishWorkerQueue = [message].concat(this.dishWorkerQueue); + } else { + this.dishWorkerQueue.push(message); + } + } + + /** + * Sends a message to the DishWorker + * + * @param {object} message + * @param {string} message.action + * @param {object} message.data + */ + postDishMessage(message) { + if (this.dishWorker.currentAction !== "") { + this.queueDishMessage(message); + } else { + this.dishWorker.currentAction = message.action; + this.dishWorker.worker.postMessage(message); + } + } + /** * Sets the console log level in the workers. */