From bbea58dabbc2fbbcf2bd798b4cc77b36191cb15e Mon Sep 17 00:00:00 2001 From: Michael Rowley Date: Mon, 7 Nov 2022 18:07:45 +0000 Subject: [PATCH] Added support for multiple tabs in URL This adds support for storing multiple tabs' contents and headers in the URL (with backwards compatibility for the current standard '&input=...') and moves the tab header information to the InputWorker instead of an array in the TabWaiter. --- src/web/App.mjs | 69 +++++++++--------- src/web/html/index.html | 7 ++ src/web/index.js | 3 +- src/web/waiters/ControlsWaiter.mjs | 67 ++++++++++-------- src/web/waiters/InputWaiter.mjs | 108 +++++++++++++++++++---------- src/web/waiters/TabWaiter.mjs | 72 +++---------------- src/web/workers/InputWorker.mjs | 47 +++++++++++-- 7 files changed, 206 insertions(+), 167 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index 9d4813e0..15f664d9 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -212,29 +212,6 @@ class App { this.manager.worker.silentBake(recipeConfig); } - - /** - * Sets the user's input data. - * - * @param {string} input - The string to set the input to - */ - setInput(input) { - // Get the currently active tab. - // If there isn't one, assume there are no inputs so use inputNum of 1 - let inputNum = this.manager.tabs.getActiveInputTab(); - if (inputNum === -1) inputNum = 1; - this.manager.input.updateInputValue(inputNum, input); - - this.manager.input.inputWorker.postMessage({ - action: "setInput", - data: { - inputNum: inputNum, - silent: true - } - }); - } - - /** * Populates the operations accordion list with the categories and operations specified in the * view constructor. @@ -483,12 +460,36 @@ class App { } // Read in input data from URI params - if (this.uriParams.input) { - try { - const inputData = fromBase64(this.uriParams.input); - this.setInput(inputData); - } catch (err) {} + this.manager.input.clearAllIoClick(); + let maxInputNum = 1; + for (const [param, value] of Object.entries(this.uriParams)) { + if (typeof param !== "string") { + continue; + } + + const inputHeaderRegex = /input\[('?[A-Za-z0-9\-_]+)'?\]/.exec(param); + if (inputHeaderRegex === null && param !== "input") { + // Invalid parameter key. + continue; + } + + if (maxInputNum > 1) { + this.manager.input.addInput(false); + } + + if (inputHeaderRegex !== null && inputHeaderRegex.length !== 1) { + // This input has a custom header that can be 'imported'. + const header = inputHeaderRegex[1]; + if (header[0] === "'") { + this.manager.input.setTabName(maxInputNum, fromBase64(header.substring(1))); + } + } + + this.manager.input.updateInputValue(maxInputNum, fromBase64(value)); + maxInputNum++; } + this.manager.input.changeTab(1, true); + this.manager.input.bakeAll(); // Read in theme from URI params if (this.uriParams.theme) { @@ -730,18 +731,16 @@ class App { this.progress = 0; this.autoBake(); - this.updateTitle(true, null, true); + this.updateTitle(true); } /** * Update the page title to contain the new recipe * - * @param {boolean} includeInput - * @param {string} input * @param {boolean} [changeUrl=true] */ - updateTitle(includeInput, input, changeUrl=true) { + updateTitle(changeUrl=true) { // Set title const recipeConfig = this.getRecipeConfig(); let title = "CyberChef"; @@ -761,8 +760,10 @@ class App { // Update the current history state (not creating a new one) if (this.options.updateUrl && changeUrl) { - this.lastStateUrl = this.manager.controls.generateStateUrl(true, includeInput, input, recipeConfig); - window.history.replaceState({}, title, this.lastStateUrl); + this.manager.controls.generateStateUrl(true, true, recipeConfig).then((lastStateUrl) => { + this.lastStateUrl = lastStateUrl; + window.history.replaceState({}, title, this.lastStateUrl); + }); } } diff --git a/src/web/html/index.html b/src/web/html/index.html index d222fee1..24d18078 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -586,6 +586,13 @@ Keep the current tab in sync between the input and output + +
+ +