mirror of
https://github.com/gchq/CyberChef
synced 2025-01-09 02:58:46 +00:00
Update handling of bake errors.
Add preview thumbnail for image input.
This commit is contained in:
parent
1cedc94652
commit
ff9c68db56
6 changed files with 193 additions and 116 deletions
|
@ -95,7 +95,7 @@ async function bake(data) {
|
||||||
self.loadRequiredModules(data.recipeConfig);
|
self.loadRequiredModules(data.recipeConfig);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
self.inputNum = data.inputNum;
|
self.inputNum = parseInt(data.inputNum, 10);
|
||||||
const response = await self.chef.bake(
|
const response = await self.chef.bake(
|
||||||
data.input, // The user's input
|
data.input, // The user's input
|
||||||
data.recipeConfig, // The configuration of the recipe
|
data.recipeConfig, // The configuration of the recipe
|
||||||
|
@ -104,13 +104,25 @@ async function bake(data) {
|
||||||
data.step // Whether or not to take one step or execute the whole recipe
|
data.step // Whether or not to take one step or execute the whole recipe
|
||||||
);
|
);
|
||||||
if (typeof response.result === "string") {
|
if (typeof response.result === "string") {
|
||||||
self.postMessage({
|
if (response.progress !== data.progress + data.recipeConfig.length) {
|
||||||
action: "bakeComplete",
|
self.postMessage({
|
||||||
data: Object.assign(response, {
|
action: "bakeError",
|
||||||
id: data.id,
|
data: {
|
||||||
inputNum: data.inputNum
|
error: response.result,
|
||||||
})
|
id: data.id,
|
||||||
});
|
inputNum: data.inputNum,
|
||||||
|
progress: response.progress
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.postMessage({
|
||||||
|
action: "bakeComplete",
|
||||||
|
data: Object.assign(response, {
|
||||||
|
id: data.id,
|
||||||
|
inputNum: data.inputNum
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
action: "bakeComplete",
|
action: "bakeComplete",
|
||||||
|
@ -123,10 +135,11 @@ async function bake(data) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
action: "bakeError",
|
action: "bakeError",
|
||||||
data: Object.assign(err, {
|
data: {
|
||||||
|
error: err,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
inputNum: data.inputNum
|
inputNum: data.inputNum
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.inputNum = -1;
|
self.inputNum = -1;
|
||||||
|
|
|
@ -79,6 +79,9 @@ class App {
|
||||||
if (!this.workerLoaded || !this.appLoaded ||
|
if (!this.workerLoaded || !this.appLoaded ||
|
||||||
!document.getElementById("loader-wrapper")) return;
|
!document.getElementById("loader-wrapper")) return;
|
||||||
|
|
||||||
|
// Bake initial input
|
||||||
|
this.getAllInput();
|
||||||
|
|
||||||
// Trigger CSS animations to remove preloader
|
// Trigger CSS animations to remove preloader
|
||||||
document.body.classList.add("loaded");
|
document.body.classList.add("loaded");
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ class InputWaiter {
|
||||||
/**
|
/**
|
||||||
* InputWaiter constructor.
|
* InputWaiter 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;
|
||||||
|
@ -341,7 +341,6 @@ class InputWaiter {
|
||||||
this.setInputInfo(inputData.input.length, lines);
|
this.setInputInfo(inputData.input.length, lines);
|
||||||
} else {
|
} else {
|
||||||
this.setFile(inputData);
|
this.setFile(inputData);
|
||||||
// show file info here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!silent) window.dispatchEvent(this.manager.statechange);
|
if (!silent) window.dispatchEvent(this.manager.statechange);
|
||||||
|
@ -372,6 +371,15 @@ class InputWaiter {
|
||||||
this.displayFilePreview(inputData);
|
this.displayFilePreview(inputData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the input thumbnail to the default icon
|
||||||
|
*/
|
||||||
|
resetFileThumb() {
|
||||||
|
const fileThumb = document.getElementById("input-file-thumbnail");
|
||||||
|
fileThumb.src = require("./static/images/file-128x128.png");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a chunk of the file in the input behind the file overlay
|
* Shows a chunk of the file in the input behind the file overlay
|
||||||
*
|
*
|
||||||
|
@ -382,11 +390,28 @@ class InputWaiter {
|
||||||
displayFilePreview(inputData) {
|
displayFilePreview(inputData) {
|
||||||
const activeTab = this.getActiveTab(),
|
const activeTab = this.getActiveTab(),
|
||||||
input = inputData.input,
|
input = inputData.input,
|
||||||
inputText = document.getElementById("input-text");
|
inputText = document.getElementById("input-text"),
|
||||||
|
fileThumb = document.getElementById("input-file-thumbnail");
|
||||||
if (inputData.inputNum !== activeTab) return;
|
if (inputData.inputNum !== activeTab) return;
|
||||||
inputText.style.overflow = "hidden";
|
inputText.style.overflow = "hidden";
|
||||||
inputText.classList.add("blur");
|
inputText.classList.add("blur");
|
||||||
inputText.value = Utils.printable(Utils.arrayBufferToStr(input));
|
inputText.value = Utils.printable(Utils.arrayBufferToStr(input.slice(0, 4096)));
|
||||||
|
|
||||||
|
// Display image in thumbnail if we want
|
||||||
|
if (this.app.options.imagePreview) {
|
||||||
|
const inputArr = new Uint8Array(input),
|
||||||
|
type = isImage(inputArr);
|
||||||
|
if (type && type !== "image/tiff" && inputArr.byteLength <= 512000) {
|
||||||
|
const blob = new Blob([inputArr], {type: type}),
|
||||||
|
url = URL.createObjectURL(blob);
|
||||||
|
fileThumb.src = url;
|
||||||
|
} else {
|
||||||
|
this.resetFileThumb();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.resetFileThumb();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -312,20 +312,29 @@ self.setInput = function(inputData) {
|
||||||
input: inputVal
|
input: inputVal
|
||||||
};
|
};
|
||||||
if (typeof inputVal !== "string") {
|
if (typeof inputVal !== "string") {
|
||||||
inputObj.input = inputVal.fileBuffer.slice(0, 4096);
|
const fileSlice = inputVal.fileBuffer.slice(0, 512001);
|
||||||
|
inputObj.input = fileSlice;
|
||||||
inputObj.name = inputVal.name;
|
inputObj.name = inputVal.name;
|
||||||
inputObj.size = inputVal.size;
|
inputObj.size = inputVal.size;
|
||||||
inputObj.type = inputVal.type;
|
inputObj.type = inputVal.type;
|
||||||
inputObj.progress = input.progress;
|
inputObj.progress = input.progress;
|
||||||
}
|
|
||||||
|
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
action: "setInput",
|
action: "setInput",
|
||||||
data: {
|
data: {
|
||||||
inputObj: inputObj,
|
inputObj: inputObj,
|
||||||
silent: silent
|
silent: silent
|
||||||
}
|
}
|
||||||
});
|
}, [fileSlice]);
|
||||||
|
} else {
|
||||||
|
self.postMessage({
|
||||||
|
action: "setInput",
|
||||||
|
data: {
|
||||||
|
inputObj: inputObj,
|
||||||
|
silent: silent
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
self.getInputProgress(inputNum);
|
self.getInputProgress(inputNum);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -132,15 +132,17 @@ class OutputWaiter {
|
||||||
*
|
*
|
||||||
* @param {Error} error
|
* @param {Error} error
|
||||||
* @param {number} inputNum
|
* @param {number} inputNum
|
||||||
|
* @param {number} [progress=0]
|
||||||
*/
|
*/
|
||||||
updateOutputError(error, inputNum) {
|
updateOutputError(error, inputNum, progress=0) {
|
||||||
if (this.getOutput(inputNum) === -1) return;
|
if (this.getOutput(inputNum) === -1) return;
|
||||||
|
|
||||||
this.outputs[inputNum].error = error;
|
this.outputs[inputNum].error = error;
|
||||||
|
this.outputs[inputNum].progress = progress;
|
||||||
|
this.updateOutputStatus("error", inputNum);
|
||||||
|
|
||||||
// call handle error here
|
// call handle error here
|
||||||
// or make the error handling part of set()
|
// or make the error handling part of set()
|
||||||
this.set(inputNum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,6 +155,11 @@ class OutputWaiter {
|
||||||
if (this.getOutput(inputNum) === -1) return;
|
if (this.getOutput(inputNum) === -1) return;
|
||||||
this.outputs[inputNum].status = status;
|
this.outputs[inputNum].status = status;
|
||||||
|
|
||||||
|
if (status !== "error") {
|
||||||
|
delete this.outputs[inputNum].error;
|
||||||
|
delete this.outputs[inputNum].progress;
|
||||||
|
}
|
||||||
|
|
||||||
this.set(inputNum);
|
this.set(inputNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,9 +193,10 @@ class OutputWaiter {
|
||||||
set(inputNum) {
|
set(inputNum) {
|
||||||
const output = this.outputs[inputNum];
|
const output = this.outputs[inputNum];
|
||||||
if (output === undefined || output === null) return;
|
if (output === undefined || output === null) return;
|
||||||
|
|
||||||
if (typeof inputNum !== "number") inputNum = parseInt(inputNum, 10);
|
if (typeof inputNum !== "number") inputNum = parseInt(inputNum, 10);
|
||||||
|
|
||||||
|
if (inputNum !== this.getActiveTab()) return;
|
||||||
|
|
||||||
const outputText = document.getElementById("output-text");
|
const outputText = document.getElementById("output-text");
|
||||||
const outputHtml = document.getElementById("output-html");
|
const outputHtml = document.getElementById("output-html");
|
||||||
const outputFile = document.getElementById("output-file");
|
const outputFile = document.getElementById("output-file");
|
||||||
|
@ -204,11 +212,35 @@ class OutputWaiter {
|
||||||
this.manager.controls.hideStaleIndicator();
|
this.manager.controls.hideStaleIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.status === "inactive") {
|
this.manager.recipe.updateBreakpointIndicator(false);
|
||||||
// An output is inactive when it has been created but has not been baked at all
|
|
||||||
// show a blank here
|
if (output.status === "pending" || output.status === "baking") {
|
||||||
if (inputNum === this.getActiveTab()) {
|
// show the loader and the status message if it's being shown
|
||||||
this.toggleLoader(false);
|
// otherwise don't do anything
|
||||||
|
this.toggleLoader(true);
|
||||||
|
document.querySelector("#output-loader .loading-msg").textContent = output.statusMessage;
|
||||||
|
|
||||||
|
} else if (output.status === "error") {
|
||||||
|
// style the tab if it's being shown
|
||||||
|
// run app.handleError()
|
||||||
|
this.toggleLoader(false);
|
||||||
|
outputText.style.display = "block";
|
||||||
|
outputHtml.style.display = "none";
|
||||||
|
outputFile.style.display = "none";
|
||||||
|
outputHighlighter.display = "none";
|
||||||
|
inputHighlighter.display = "none";
|
||||||
|
|
||||||
|
outputText.value = output.error;
|
||||||
|
outputHtml.innerHTML = "";
|
||||||
|
|
||||||
|
this.manager.recipe.updateBreakpointIndicator(output.progress);
|
||||||
|
} else if (output.status === "baked" || output.status === "inactive") {
|
||||||
|
this.displayTabInfo(inputNum);
|
||||||
|
this.toggleLoader(false);
|
||||||
|
this.closeFile();
|
||||||
|
let scriptElements, lines, length;
|
||||||
|
|
||||||
|
if (output.data === null) {
|
||||||
outputText.style.display = "block";
|
outputText.style.display = "block";
|
||||||
outputHtml.style.display = "none";
|
outputHtml.style.display = "none";
|
||||||
outputFile.style.display = "none";
|
outputFile.style.display = "none";
|
||||||
|
@ -218,83 +250,63 @@ class OutputWaiter {
|
||||||
outputText.value = "";
|
outputText.value = "";
|
||||||
outputHtml.innerHTML = "";
|
outputHtml.innerHTML = "";
|
||||||
|
|
||||||
}
|
lines = 0;
|
||||||
} else if (output.status === "pending" || output.status === "baking") {
|
length = 0;
|
||||||
// show the loader and the status message if it's being shown
|
return;
|
||||||
// otherwise don't do anything
|
|
||||||
if (inputNum === this.getActiveTab()) {
|
|
||||||
this.toggleLoader(true);
|
|
||||||
document.querySelector("#output-loader .loading-msg").textContent = output.statusMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (output.status === "error") {
|
switch (output.data.type) {
|
||||||
// style the tab if it's being shown
|
case "html":
|
||||||
// run app.handleError()
|
outputText.style.display = "none";
|
||||||
if (inputNum === this.getActiveTab()) {
|
outputHtml.style.display = "block";
|
||||||
this.toggleLoader(false);
|
outputFile.style.display = "none";
|
||||||
}
|
outputHighlighter.style.display = "none";
|
||||||
} else if (output.status === "baked") {
|
inputHighlighter.style.display = "none";
|
||||||
// Display the output if it's the active tab
|
|
||||||
this.displayTabInfo(inputNum);
|
|
||||||
if (inputNum === this.getActiveTab()) {
|
|
||||||
this.toggleLoader(false);
|
|
||||||
this.closeFile();
|
|
||||||
let scriptElements, lines, length;
|
|
||||||
const duration = output.data.duration;
|
|
||||||
|
|
||||||
switch (output.data.type) {
|
outputText.value = "";
|
||||||
case "html":
|
outputHtml.innerHTML = output.data.result;
|
||||||
outputText.style.display = "none";
|
|
||||||
outputHtml.style.display = "block";
|
|
||||||
outputFile.style.display = "none";
|
|
||||||
outputHighlighter.style.display = "none";
|
|
||||||
inputHighlighter.style.display = "none";
|
|
||||||
|
|
||||||
outputText.value = "";
|
// Execute script sections
|
||||||
outputHtml.innerHTML = output.data.result;
|
scriptElements = outputHtml.querySelectorAll("script");
|
||||||
|
for (let i = 0; i < scriptElements.length; i++) {
|
||||||
// Execute script sections
|
try {
|
||||||
scriptElements = outputHtml.querySelectorAll("script");
|
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
|
||||||
for (let i = 0; i < scriptElements.length; i++) {
|
} catch (err) {
|
||||||
try {
|
log.error(err);
|
||||||
eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval
|
|
||||||
} catch (err) {
|
|
||||||
log.error(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
length = output.data.dish.value.length;
|
}
|
||||||
|
length = output.data.dish.value.length;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "ArrayBuffer":
|
case "ArrayBuffer":
|
||||||
outputText.style.display = "block";
|
outputText.style.display = "block";
|
||||||
outputHtml.style.display = "none";
|
outputHtml.style.display = "none";
|
||||||
outputHighlighter.display = "none";
|
outputHighlighter.display = "none";
|
||||||
inputHighlighter.display = "none";
|
inputHighlighter.display = "none";
|
||||||
|
|
||||||
outputText.value = "";
|
outputText.value = "";
|
||||||
outputHtml.innerHTML = "";
|
outputHtml.innerHTML = "";
|
||||||
|
|
||||||
length = output.data.result.length;
|
length = output.data.result.length;
|
||||||
this.setFile(output.data.result);
|
this.setFile(output.data.result);
|
||||||
break;
|
break;
|
||||||
case "string":
|
case "string":
|
||||||
default:
|
default:
|
||||||
outputText.style.display = "block";
|
outputText.style.display = "block";
|
||||||
outputHtml.style.display = "none";
|
outputHtml.style.display = "none";
|
||||||
outputFile.style.display = "none";
|
outputFile.style.display = "none";
|
||||||
outputHighlighter.display = "block";
|
outputHighlighter.display = "block";
|
||||||
inputHighlighter.display = "block";
|
inputHighlighter.display = "block";
|
||||||
|
|
||||||
outputText.value = Utils.printable(output.data.result, true);
|
outputText.value = Utils.printable(output.data.result, true);
|
||||||
outputHtml.innerHTML = "";
|
outputHtml.innerHTML = "";
|
||||||
|
|
||||||
lines = output.data.result.count("\n") + 1;
|
lines = output.data.result.count("\n") + 1;
|
||||||
length = output.data.result.length;
|
length = output.data.result.length;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
this.setOutputInfo(length, lines, duration);
|
|
||||||
this.backgroundMagic();
|
|
||||||
}
|
}
|
||||||
|
this.setOutputInfo(length, lines, output.data.duration);
|
||||||
|
this.backgroundMagic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,7 +567,7 @@ class OutputWaiter {
|
||||||
*/
|
*/
|
||||||
goToTab() {
|
goToTab() {
|
||||||
const tabNum = parseInt(window.prompt("Enter tab number:", this.getActiveTab().toString()), 10);
|
const tabNum = parseInt(window.prompt("Enter tab number:", this.getActiveTab().toString()), 10);
|
||||||
if (this.getOutputIndex(tabNum) >= 0) {
|
if (this.getOutput(tabNum) !== undefined) {
|
||||||
this.changeTab(tabNum, this.app.options.syncTabs);
|
this.changeTab(tabNum, this.app.options.syncTabs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,24 +137,13 @@ class WorkerWaiter {
|
||||||
case "bakeComplete":
|
case "bakeComplete":
|
||||||
log.debug(`Bake ${inputNum} complete.`);
|
log.debug(`Bake ${inputNum} complete.`);
|
||||||
this.updateOutput(r.data, r.data.inputNum);
|
this.updateOutput(r.data, r.data.inputNum);
|
||||||
|
this.workerFinished(currentWorker);
|
||||||
if (this.inputs.length > 0) {
|
|
||||||
this.bakeNextInput(this.chefWorkers.indexOf(currentWorker));
|
|
||||||
} else {
|
|
||||||
// The ChefWorker is no longer needed
|
|
||||||
log.debug("No more inputs to bake. Closing ChefWorker.");
|
|
||||||
currentWorker.active = false;
|
|
||||||
this.removeChefWorker(currentWorker);
|
|
||||||
|
|
||||||
const progress = this.getBakeProgress();
|
|
||||||
if (progress.total === progress.baked) {
|
|
||||||
this.bakingComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "BakeError":
|
case "bakeError":
|
||||||
this.manager.output.updateOutputError(r.data, inputNum);
|
if (!r.data.hasOwnProperty("progress")) this.app.handleError(r.data.error);
|
||||||
|
this.manager.output.updateOutputError(r.data.error, inputNum, r.data.progress);
|
||||||
|
this.workerFinished(currentWorker);
|
||||||
// do more here
|
// do more here
|
||||||
break;
|
break;
|
||||||
case "dishReturned":
|
case "dishReturned":
|
||||||
|
@ -196,7 +185,6 @@ class WorkerWaiter {
|
||||||
* @param {number} inputNum
|
* @param {number} inputNum
|
||||||
*/
|
*/
|
||||||
updateOutput(data, inputNum) {
|
updateOutput(data, inputNum) {
|
||||||
|
|
||||||
this.manager.output.updateOutputValue(data, inputNum);
|
this.manager.output.updateOutputValue(data, inputNum);
|
||||||
this.manager.output.updateOutputStatus("baked", inputNum);
|
this.manager.output.updateOutputStatus("baked", inputNum);
|
||||||
|
|
||||||
|
@ -242,14 +230,40 @@ class WorkerWaiter {
|
||||||
*/
|
*/
|
||||||
cancelBake() {
|
cancelBake() {
|
||||||
for (let i = this.chefWorkers.length - 1; i >= 0; i--) {
|
for (let i = this.chefWorkers.length - 1; i >= 0; i--) {
|
||||||
|
const inputNum = this.chefWorkers[i].inputNum;
|
||||||
this.removeChefWorker(this.chefWorkers[i]);
|
this.removeChefWorker(this.chefWorkers[i]);
|
||||||
|
this.manager.output.updateOutputStatus("inactive", inputNum);
|
||||||
}
|
}
|
||||||
this.setBakingStatus(false);
|
this.setBakingStatus(false);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.inputs.length; i++) {
|
||||||
|
this.manager.output.updateOutputStatus("inactive", this.inputs[i].inputNum);
|
||||||
|
}
|
||||||
|
|
||||||
this.inputs = [];
|
this.inputs = [];
|
||||||
this.totalOutputs = 0;
|
this.totalOutputs = 0;
|
||||||
this.manager.controls.showStaleIndicator();
|
this.manager.controls.showStaleIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a worker completing baking
|
||||||
|
*/
|
||||||
|
workerFinished(workerObj) {
|
||||||
|
if (this.inputs.length > 0) {
|
||||||
|
this.bakeNextInput(this.chefWorkers.indexOf(workerObj));
|
||||||
|
} else {
|
||||||
|
// The ChefWorker is no longer needed
|
||||||
|
log.debug("No more inputs to bake. Closing ChefWorker.");
|
||||||
|
workerObj.active = false;
|
||||||
|
this.removeChefWorker(workerObj);
|
||||||
|
|
||||||
|
const progress = this.getBakeProgress();
|
||||||
|
if (progress.total === progress.baked) {
|
||||||
|
this.bakingComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for completed bakes
|
* Handler for completed bakes
|
||||||
*/
|
*/
|
||||||
|
@ -371,6 +385,7 @@ class WorkerWaiter {
|
||||||
*/
|
*/
|
||||||
queueInput(inputData) {
|
queueInput(inputData) {
|
||||||
this.manager.output.updateOutputStatus("pending", inputData.inputNum);
|
this.manager.output.updateOutputStatus("pending", inputData.inputNum);
|
||||||
|
this.manager.output.updateOutputMessage(`Input ${inputData.inputNum} has not been baked yet.`);
|
||||||
|
|
||||||
this.totalOutputs++;
|
this.totalOutputs++;
|
||||||
this.inputs.push(inputData);
|
this.inputs.push(inputData);
|
||||||
|
|
Loading…
Reference in a new issue