Improve WorkerWaiter and OutputWaiter.

- Will run and display outputs in the output area as they're baked
- Creates output tabs
- Can change output tabs (only the first 4 at the moment)
This commit is contained in:
j433866 2019-04-02 16:58:36 +01:00
parent 2cb33bfec4
commit e0c9aba25e
5 changed files with 814 additions and 727 deletions

View File

@ -90,7 +90,7 @@ class InputWaiter {
} }
/** /**
* Removes a loaderworker using inputNum * Removes a loaderworker
* *
* @param {Object} workerObj * @param {Object} workerObj
*/ */
@ -203,14 +203,6 @@ class InputWaiter {
this.inputs.splice(i, 1); this.inputs.splice(i, 1);
} }
} }
// if (this.inputs.length === 0) {
// this.inputs.push({
// inputNum: inputNum,
// data: "",
// status: "loaded",
// progress: 100
// });
// }
} }
/** /**
@ -327,14 +319,11 @@ class InputWaiter {
if (index === -1) { if (index === -1) {
return null; return null;
} }
if (this.inputs[index].inputNum === inputNum) { if (typeof this.inputs[index].data === "string") {
if (typeof this.inputs[index].data === "string") { return this.inputs[index].data;
return this.inputs[index].data; } else {
} else { return this.inputs[index].data.fileBuffer;
return this.inputs[index].data.fileBuffer;
}
} }
return null;
} }
/** /**
@ -359,7 +348,8 @@ class InputWaiter {
const value = (textArea.value !== undefined) ? textArea.value : ""; const value = (textArea.value !== undefined) ? textArea.value : "";
const inputNum = this.getActiveTab(); const inputNum = this.getActiveTab();
if (this.getInput(inputNum) === null || typeof this.getInput(inputNum) === "string") { const input = this.getInput(inputNum);
if (input === null || typeof input === "string") {
this.updateInputValue(inputNum, value); this.updateInputValue(inputNum, value);
} }
@ -774,6 +764,8 @@ class InputWaiter {
progress: 100 progress: 100
}); });
this.manager.output.addOutput(inputNum, changeTab);
const tabsWrapper = document.getElementById("input-tabs"); const tabsWrapper = document.getElementById("input-tabs");
const numTabs = tabsWrapper.children.length; const numTabs = tabsWrapper.children.length;
@ -831,6 +823,8 @@ class InputWaiter {
} }
this.refreshTabs(activeTab); this.refreshTabs(activeTab);
} }
this.manager.output.removeTab(inputNum);
} }
/** /**
@ -883,6 +877,8 @@ class InputWaiter {
} }
this.changeTab(activeTab); this.changeTab(activeTab);
// MAKE THE OUTPUT REFRESH TOO
} }
/** /**
@ -966,14 +962,8 @@ class InputWaiter {
* @param {number} inputNum * @param {number} inputNum
*/ */
changeTab(inputNum) { changeTab(inputNum) {
const inputIdx = this.getInputIndex(inputNum); const currentNum = this.getActiveTab();
let currentIdx = -1; if (this.getInputIndex(inputNum) === -1) return;
try {
currentIdx = this.getActiveTab();
} catch (err) {}
if (inputIdx === -1) {
return;
}
const tabsWrapper = document.getElementById("input-tabs"); const tabsWrapper = document.getElementById("input-tabs");
const tabs = tabsWrapper.children; const tabs = tabsWrapper.children;
@ -990,7 +980,7 @@ class InputWaiter {
if (!found) { if (!found) {
// Shift the tabs here // Shift the tabs here
let direction = "right"; let direction = "right";
if (currentIdx > inputIdx) { if (currentNum > inputNum) {
direction = "left"; direction = "left";
} }
@ -1122,9 +1112,8 @@ class InputWaiter {
const activeTab = activeTabs.item(0); const activeTab = activeTabs.item(0);
const tabNum = activeTab.getAttribute("inputNum"); const tabNum = activeTab.getAttribute("inputNum");
return parseInt(tabNum, 10); return parseInt(tabNum, 10);
} else {
return -1;
} }
return -1;
} }
/** /**

View File

@ -84,7 +84,7 @@ class Manager {
setup() { setup() {
this.input.addTab(); this.input.addTab();
this.input.setupLoaderWorker(); this.input.setupLoaderWorker();
this.worker.setupChefWorkers(); this.worker.setupChefWorker();
this.recipe.initialiseOperationDragNDrop(); this.recipe.initialiseOperationDragNDrop();
this.controls.initComponents(); this.controls.initComponents();
this.controls.autoBakeChange(); this.controls.autoBakeChange();
@ -165,24 +165,24 @@ class Manager {
this.addDynamicListener("#input-tabs li .input-tab-content", "click", this.input.changeTabClick, this.input); this.addDynamicListener("#input-tabs li .input-tab-content", "click", this.input.changeTabClick, this.input);
// Output // Output
document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output)); // document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
document.getElementById("copy-output").addEventListener("click", this.output.copyClick.bind(this.output)); // document.getElementById("copy-output").addEventListener("click", this.output.copyClick.bind(this.output));
document.getElementById("switch").addEventListener("click", this.output.switchClick.bind(this.output)); // document.getElementById("switch").addEventListener("click", this.output.switchClick.bind(this.output));
document.getElementById("undo-switch").addEventListener("click", this.output.undoSwitchClick.bind(this.output)); // document.getElementById("undo-switch").addEventListener("click", this.output.undoSwitchClick.bind(this.output));
document.getElementById("maximise-output").addEventListener("click", this.output.maximiseOutputClick.bind(this.output)); // document.getElementById("maximise-output").addEventListener("click", this.output.maximiseOutputClick.bind(this.output));
document.getElementById("magic").addEventListener("click", this.output.magicClick.bind(this.output)); // document.getElementById("magic").addEventListener("click", this.output.magicClick.bind(this.output));
document.getElementById("output-text").addEventListener("scroll", this.highlighter.outputScroll.bind(this.highlighter)); // document.getElementById("output-text").addEventListener("scroll", this.highlighter.outputScroll.bind(this.highlighter));
document.getElementById("output-text").addEventListener("mouseup", this.highlighter.outputMouseup.bind(this.highlighter)); // document.getElementById("output-text").addEventListener("mouseup", this.highlighter.outputMouseup.bind(this.highlighter));
document.getElementById("output-text").addEventListener("mousemove", this.highlighter.outputMousemove.bind(this.highlighter)); // document.getElementById("output-text").addEventListener("mousemove", this.highlighter.outputMousemove.bind(this.highlighter));
document.getElementById("output-html").addEventListener("mouseup", this.highlighter.outputHtmlMouseup.bind(this.highlighter)); // document.getElementById("output-html").addEventListener("mouseup", this.highlighter.outputHtmlMouseup.bind(this.highlighter));
document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter)); // document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter));
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter); // this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter); // this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output); // this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output); // this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output);
document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output)); // 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(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output);
// this.addDynamicListener("#output-tabs ul li .output-tab-content", "click", this.output.changeTabClick, this.output); this.addDynamicListener("#output-tabs-wrapper #output-tabs li .output-tab-content", "click", this.output.changeTabClick, this.output);
// Options // Options
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options)); document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));

File diff suppressed because it is too large Load Diff

View File

@ -8,85 +8,170 @@
import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker"; import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker";
/** /**
* Waiter to handle conversations with the ChefWorker. * Waiter to handle conversations with the ChefWorker
*/ */
class WorkerWaiter { class WorkerWaiter {
/** /**
* WorkerWaiter constructor. * WorkerWaiter constructor
* *
* @param {App} app - The main view object for CyberChef * @param {App} app - The main view object for CyberChef
* @param {Manager} manager - The CyberChef event manager. * @param {Manager} manager - The CyberChef event manager
*/ */
constructor(app, manager) { constructor(app, manager) {
this.app = app; this.app = app;
this.manager = manager; this.manager = manager;
this.callbacks = {};
this.callbackID = 0;
this.pendingInputs = [];
this.runningWorkers = 0;
this.chefWorkers = []; this.chefWorkers = [];
this.outputs = []; this.maxWorkers = navigator.hardwareConcurrency || 4;
this.inputs = [];
this.totalOutputs = 0;
} }
/** /**
* Sets up a pool of ChefWorkers to be used for baking * Terminates any existing ChefWorkers and sets up a new worker
*/ */
setupChefWorkers() { setupChefWorker() {
const threads = navigator.hardwareConcurrency || 4; // Default to 4 for (let i = 0; i < this.chefWorkers.length; i++) {
const worker = this.chefWorkers.pop();
worker.terminate();
}
for (let i = 0; i < threads; i++) { this.addChefWorker();
const newWorker = new ChefWorker(); }
newWorker.addEventListener("message", this.handleChefMessage.bind(this));
let docURL = document.location.href.split(/[#?]/)[0]; /**
const index = docURL.lastIndexOf("/"); * Adds a new ChefWorker
if (index > 0) { *
docURL = docURL.substring(0, index); * @returns {number} The index of the created worker
*/
addChefWorker() {
// First find if there are any inactive workers, as this will be
// more efficient than creating a new one
for (let i = 0; i < this.chefWorkers.length; i++) {
if (!this.chefWorkers[i].active) {
return i;
} }
newWorker.postMessage({"action": "docURL", "data": docURL}); }
newWorker.postMessage({"action": "inputNum", "data": 0});
this.chefWorkers.push({ if (this.chefWorkers.length === this.maxWorkers) {
worker: newWorker, // Can't create any more workers
inputNum: 0 return -1;
}); }
log.debug("Adding new ChefWorker");
// Create a new ChefWorker and send it the docURL
const newWorker = new ChefWorker();
newWorker.addEventListener("message", this.handleChefMessage.bind(this));
let docURL = document.location.href.split(/[#?]/)[0];
const index = docURL.lastIndexOf("/");
if (index > 0) {
docURL = docURL.substring(0, index);
}
newWorker.postMessage({"action": "docURL", "data": docURL});
// Store the worker, whether or not it's active, and the inputNum as an object
const newWorkerObj = {
worker: newWorker,
active: false,
inputNum: this.manager.input.getActiveTab()
};
this.chefWorkers.push(newWorkerObj);
return this.chefWorkers.indexOf(newWorkerObj);
}
/**
* Removes a ChefWorker
*
* @param {Object} workerObj
*/
removeChefWorker(workerObj) {
const index = this.chefWorkers.indexOf(workerObj);
if (index === -1) {
return;
}
this.chefWorkers[index].worker.terminate();
this.chefWorkers.splice(index, 1);
// There should always be a ChefWorker loaded
if (this.chefWorkers.length === 0) {
this.addChefWorker();
} }
} }
/**
* Finds and returns the object for the ChefWorker of a given inputNum
*
* @param {number} inputNum
*/
getChefWorker(inputNum) {
for (let i = 0; i < this.chefWorkers.length; i++) {
if (this.chefWorkers[i].inputNum === inputNum) {
return this.chefWorkers[i];
}
}
}
/** /**
* Handler for messages sent back by the ChefWorker. * Handler for messages sent back by the ChefWorkers
* *
* @param {MessageEvent} e * @param {MessageEvent} e
*/ */
handleChefMessage(e) { handleChefMessage(e) {
const r = e.data; const r = e.data;
log.debug("Receiving '" + r.action + "' from ChefWorker"); let inputNum = 0;
log.debug(`Receiving ${r.action} from ChefWorker.`);
if (r.data.hasOwnProperty("inputNum")) {
inputNum = r.data.inputNum;
}
const currentWorker = this.getChefWorker(inputNum);
switch (r.action) { switch (r.action) {
case "bakeComplete": case "bakeComplete":
this.runningWorkers -= 1; log.debug(`Bake ${inputNum} complete.`);
this.outputs.push({ this.updateOutput(r.data, r.data.inputNum);
data: r.data,
inputNum: r.data.inputNum if (this.inputs.length > 0) {
}); const nextInput = this.inputs.pop();
if (this.pendingInputs.length > 0) { log.debug(`Baking input ${nextInput.inputNum}.`);
log.debug(`Bake ${r.data.inputNum} complete. Baking next input`); this.manager.output.updateOutputStatus("baking", nextInput.inputNum);
this.bakeNextInput(r.data.inputNum); this.manager.output.updateOutputMessage("Baking...", nextInput.inputNum);
} else if (this.runningWorkers <= 0) { currentWorker.inputNum = nextInput.inputNum;
this.runningWorkers = 0; currentWorker.active = true;
this.recipeConfig = undefined; currentWorker.worker.postMessage({
this.options = undefined; action: "bake",
this.progress = undefined; data: {
this.step = undefined; input: nextInput.input,
this.bakingComplete(); recipeConfig: nextInput.recipeConfig,
options: nextInput.options,
progress: nextInput.progress,
step: nextInput.step,
inputNum: nextInput.inputNum
}
});
this.displayProgress();
} else {
// The ChefWorker is no longer needed
log.debug("No more inputs to bake. Closing ChefWorker.");
this.removeChefWorker(currentWorker);
this.displayProgress();
const progress = this.getBakeProgress();
if (progress.total === progress.baked) {
this.bakingComplete();
}
} }
break; break;
case "bakeError": case "BakeError":
this.runningWorkers -= 1; this.manager.output.updateOutputError(r.data, inputNum);
this.app.handleError(r.data); // do more here
this.setBakingStatus(false);
break; break;
case "dishReturned": case "dishReturned":
this.callbacks[r.data.id](r.data); this.callbacks[r.data.id](r.data);
@ -95,17 +180,20 @@ class WorkerWaiter {
break; break;
case "workerLoaded": case "workerLoaded":
this.app.workerLoaded = true; this.app.workerLoaded = true;
log.debug("ChefWorker loaded"); log.debug("ChefWorker loaded.");
this.app.loaded(); this.app.loaded();
break; break;
case "statusMessage": case "statusMessage":
this.manager.output.setStatusMsg(r.data); // Status message should be done per output
// log.error(r);
this.manager.output.updateOutputMessage(r.data.message, r.data.inputNum);
break; break;
case "optionUpdate": case "optionUpdate":
log.debug(`Setting ${r.data.option} to ${r.data.value}`); log.debug(`Setting ${r.data.option} to ${r.data.value}`);
this.app.options[r.data.option] = r.data.value; this.app.options[r.data.option] = r.data.value;
break; break;
case "setRegisters": case "setRegisters":
// Should this update with the tabs?
this.manager.recipe.setRegisters(r.data.opIndex, r.data.numPrevRegisters, r.data.registers); this.manager.recipe.setRegisters(r.data.opIndex, r.data.numPrevRegisters, r.data.registers);
break; break;
case "highlightsCalculated": case "highlightsCalculated":
@ -117,78 +205,96 @@ class WorkerWaiter {
} }
} }
/**
* Update the value of an output
*
* @param {Object} data
* @param {number} inputNum
*/
updateOutput(data, inputNum) {
this.manager.output.updateOutputValue(data, inputNum);
this.manager.output.updateOutputStatus("baked", inputNum);
this.manager.recipe.updateBreakpointIndicator(this.app.progress);
}
/** /**
* Updates the UI to show if baking is in process or not. * Updates the UI to show if baking is in process or not.
* *
* @param {bakingStatus} * @param {boolean} bakingStatus
*/ */
setBakingStatus(bakingStatus) { setBakingStatus(bakingStatus) {
this.app.baking = bakingStatus; this.app.baking = bakingStatus;
this.manager.controls.toggleBakeButtonFunction(bakingStatus);
this.manager.output.toggleLoader(bakingStatus);
} }
/**
* Get the progress of the ChefWorkers
*/
getBakeProgress() {
const pendingInputs = this.inputs.length;
let bakingInputs = 0;
for (let i = 0; i < this.chefWorkers.length; i++) {
if (this.chefWorkers[i].active) {
bakingInputs++;
}
}
const total = this.totalOutputs;
const bakedInputs = total - pendingInputs - bakingInputs;
return {
total: total,
pending: pendingInputs,
baking: bakingInputs,
baked: bakedInputs
};
}
/** /**
* Calcels the current bake by terminating and removing all ChefWorkers, * Cancels the current bake by terminating and removing all ChefWorkers
* and creating a new one
*/ */
cancelBake() { cancelBake() {
for (let i = this.chefWorkers.length - 1; i >= 0; i--) { for (let i = this.chefWorkers.length - 1; i >= 0; i--) {
this.chefWorkers[i].worker.terminate(); this.removeChefWorker(this.chefWorkers[i]);
this.chefWorkers.pop();
} }
this.setupChefWorkers();
this.setBakingStatus(false); this.setBakingStatus(false);
this.inputs = [];
this.totalOutputs = 0;
this.manager.controls.showStaleIndicator(); this.manager.controls.showStaleIndicator();
this.displayProgress();
} }
/** /**
* Handler for completed bakes * Handler for completed bakes
*/ */
bakingComplete() { bakingComplete() {
this.setBakingStatus(false); this.setBakingStatus(false);
if (this.pendingInputs.length !== 0) return; // look into changing this to something better
// for (let i = 0; i < this.outputs.length; i++) {
// if (this.outputs[i].data.error) {
// this.app.handleError(this.outputs[i].error);
// }
// }
for (let i = 0; i < this.outputs.length; i++) { // What are these for?
if (this.outputs[i].error) { // Should be a value for each input, not just one
this.app.handleError(this.outputs[i].error); // this.app.progress = this.outputs[0].data.progress;
} // this.app.dish = this.outputs[0].data.dish;
}
this.app.progress = this.outputs[0].data.progress;
this.app.dish = this.outputs[0].data.dish;
this.manager.recipe.updateBreakpointIndicator(this.app.progress); this.manager.recipe.updateBreakpointIndicator(this.app.progress);
this.manager.output.multiSet(this.outputs); // Don't need to update the output here as updateOutput() will take care of that
document.getElementById("bake").style.background = "";
this.totalOutputs = 0; // Reset for next time
log.debug("--- Bake complete ---"); log.debug("--- Bake complete ---");
} }
/** /**
* Handler for completed bakes * Bakes the current input using the current recipe.
* * Either sends the input and recipe to a ChefWorker,
* @param {Object} response * or, if there's already the max running, adds it to inputs
*/
bakingCompleteOld(response) {
this.setBakingStatus(false);
if (!response) return;
if (response.error) {
this.app.handleError(response.error);
}
this.app.progress = response.progress;
this.app.dish = response.dish;
this.manager.recipe.updateBreakpointIndicator(response.progress);
this.manager.output.set(response.result, response.type, response.duration);
log.debug("--- Bake complete ---");
}
/**
* Asks the ChefWorker to bake the current input using the current recipe.
* *
* @param {string | Array} input * @param {string | Array} input
* @param {Object[]} recipeConfig * @param {Object[]} recipeConfig
@ -199,67 +305,55 @@ class WorkerWaiter {
bake(input, recipeConfig, options, progress, step) { bake(input, recipeConfig, options, progress, step) {
this.setBakingStatus(true); this.setBakingStatus(true);
this.recipeConfig = recipeConfig;
this.options = options;
this.progress = progress;
this.step = step;
this.outputs = [];
if (typeof input === "string") { if (typeof input === "string") {
input = [{ input = [{
input: input, input: input,
inputNum: 0 inputNum: this.manager.input.getActiveTab()
}]; }];
} }
const initialInputs = input.slice(0, this.chefWorkers.length); for (let i = 0; i < input.length; i++) {
this.pendingInputs = input.slice(this.chefWorkers.length, input.length); this.totalOutputs++;
this.runningWorkers = 0; this.manager.output.updateOutputStatus("pending", input[i].inputNum);
this.manager.output.updateOutputMessage(`Input ${input[i].inputNum} has not been baked yet.`, input[i].inputNum);
for (let i = 0; i < initialInputs.length; i++) { // If an input exists for the current inputNum, remove it
this.runningWorkers += 1; for (let x = 0; x < this.inputs.length; x++) {
this.chefWorkers[i].inputNum = initialInputs[i].inputNum; if (this.inputs[x].inputNum === input[i].inputNum) {
this.chefWorkers[i].worker.postMessage({ this.inputs.splice(x, 1);
action: "bake", }
data: { }
input: initialInputs[i].input, const workerId = this.addChefWorker();
if (workerId !== -1) {
// Send the input to the ChefWorker
this.manager.output.updateOutputStatus("baking", input[i].inputNum);
this.manager.output.updateOutputMessage("Baking...", input[i].inputNum);
this.chefWorkers[workerId].active = true;
this.chefWorkers[workerId].inputNum = input[i].inputNum;
this.chefWorkers[workerId].worker.postMessage({
action: "bake",
data: {
input: input[i].input,
recipeConfig: recipeConfig,
options: options,
progress: progress,
step: step,
inputNum: input[i].inputNum
}
});
} else {
// Add the input to inputs so it can be processed when ready
this.inputs.push({
input: input[i].input,
recipeConfig: recipeConfig, recipeConfig: recipeConfig,
options: options, options: options,
progress: progress, progress: progress,
step: step, step: step,
inputNum: initialInputs[i].inputNum inputNum: input[i].inputNum
}
});
}
}
/**
*
* @param inputNum
*/
bakeNextInput(inputNum) {
this.runningWorkers += 1;
const nextInput = this.pendingInputs.pop();
for (let i = 0; i < this.chefWorkers.length; i++) {
if (this.chefWorkers[i].inputNum === inputNum) {
this.chefWorkers[i].inputNum = nextInput.inputNum;
this.chefWorkers[i].worker.postMessage({
action: "bake",
data: {
input: nextInput.input,
recipeConfig: this.recipeConfig,
options: this.options,
progress: this.progress,
step: this.step,
inputNum: nextInput.inputNum
}
}); });
} }
} }
} }
/** /**
* Asks the ChefWorker to run a silent bake, forcing the browser to load and cache all the relevant * Asks the ChefWorker to run a silent bake, forcing the browser to load and cache all the relevant
* JavaScript code needed to do a real bake. * JavaScript code needed to do a real bake.
@ -267,7 +361,11 @@ class WorkerWaiter {
* @param {Object[]} [recipeConfig] * @param {Object[]} [recipeConfig]
*/ */
silentBake(recipeConfig) { silentBake(recipeConfig) {
this.chefWorkers[0].worker.postMessage({ // If there aren't any active ChefWorkers, addChefWorker will
// return an inactive worker instead of creating a new one
const workerId = this.addChefWorker();
if (workerId === -1) return;
this.chefWorkers[workerId].worker.postMessage({
action: "silentBake", action: "silentBake",
data: { data: {
recipeConfig: recipeConfig recipeConfig: recipeConfig
@ -275,28 +373,6 @@ class WorkerWaiter {
}); });
} }
/**
* Asks the ChefWorker to calculate highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
*/
highlight(recipeConfig, direction, pos) {
this.chefWorkers[0].postMessage({
action: "highlight",
data: {
recipeConfig: recipeConfig,
direction: direction,
pos: pos
}
});
}
/** /**
* Asks the ChefWorker to return the dish as the specified type * Asks the ChefWorker to return the dish as the specified type
* *
@ -306,8 +382,11 @@ class WorkerWaiter {
*/ */
getDishAs(dish, type, callback) { getDishAs(dish, type, callback) {
const id = this.callbackID++; const id = this.callbackID++;
const workerId = this.addChefWorker();
if (workerId === -1) return;
this.callbacks[id] = callback; this.callbacks[id] = callback;
this.chefWorkers[0].worker.postMessage({ this.chefWorkers[workerId].worker.postMessage({
action: "getDishAs", action: "getDishAs",
data: { data: {
dish: dish, dish: dish,
@ -317,15 +396,12 @@ class WorkerWaiter {
}); });
} }
/** /**
* Sets the console log level in the worker. * Sets the console log level in the workers.
* *
* @param {string} level * @param {string} level
*/ */
setLogLevel(level) { setLogLevel(level) {
if (!this.chefWorkers || !this.chefWorkers.length > 0) return;
for (let i = 0; i < this.chefWorkers.length; i++) { for (let i = 0; i < this.chefWorkers.length; i++) {
this.chefWorkers[i].worker.postMessage({ this.chefWorkers[i].worker.postMessage({
action: "setLogLevel", action: "setLogLevel",
@ -333,7 +409,46 @@ class WorkerWaiter {
}); });
} }
} }
/**
* Display the bake progress in the output bar and bake button
*/
displayProgress() {
const progress = this.getBakeProgress();
const percentComplete = ((progress.pending + progress.baking) / progress.total) * 100;
const bakeButton = document.getElementById("bake");
if (this.app.baking) {
if (percentComplete < 100) {
document.getElementById("bake").style.background = `linear-gradient(to left, #fea79a ${percentComplete}%, #f44336 ${percentComplete}%)`;
} else {
bakeButton.style.background = "";
}
} else {
// not baking
bakeButton.style.background = "";
}
const bakeInfo = document.getElementById("bake-info");
let width = progress.total.toString().length;
width = width < 2 ? 2 : width;
const totalStr = progress.total.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
const bakedStr = progress.baked.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
const pendingStr = progress.pending.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
const bakingStr = progress.baking.toString().padStart(width, " ").replace(/ /g, "&nbsp;");
let msg = "Total: " + totalStr;
msg += "<br>Baked: " + bakedStr;
if (progress.pending > 0) {
msg += "<br>Pending: " + pendingStr;
} else if (progress.baking > 0) {
msg += "<br>Baking: " + bakingStr;
}
bakeInfo.innerHTML = msg;
}
} }
export default WorkerWaiter; export default WorkerWaiter;

View File

@ -45,8 +45,8 @@
-moz-padding-start: 1px; /* Fixes bug in Firefox */ -moz-padding-start: 1px; /* Fixes bug in Firefox */
} }
#input-tabs ul, #input-tabs-wrapper #input-tabs,
#output-tabs ul { #output-tabs-wrapper #output-tabs {
list-style: none; list-style: none;
background-color: var(--title-background-colour); background-color: var(--title-background-colour);
padding: 0; padding: 0;
@ -59,8 +59,8 @@
height: var(--tab-height); height: var(--tab-height);
} }
#input-tabs ul li, #input-tabs li,
#output-tabs ul li { #output-tabs li {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
@ -73,8 +73,8 @@
vertical-align: middle; vertical-align: middle;
} }
#input-tabs ul li:hover, #input-tabs li:hover,
#output-tabs ul li:hover { #output-tabs li:hover {
cursor: pointer; cursor: pointer;
background-color: var(--primary-background-colour); background-color: var(--primary-background-colour);
} }
@ -85,6 +85,11 @@
background-color: var(--primary-background-colour); background-color: var(--primary-background-colour);
} }
.tab-buttons {
transition: 1s all ease;
display: none;
}
.input-tab-content, .input-tab-content,
.output-tab-content { .output-tab-content {
width: 100%; width: 100%;
@ -142,10 +147,10 @@
#output-loader { #output-loader {
position: absolute; position: absolute;
top: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: calc(100% - var(--title-height));
margin: 0; margin: 0;
background-color: var(--primary-background-colour); background-color: var(--primary-background-colour);
visibility: hidden; visibility: hidden;