mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-08 15:25:01 -04:00
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.
This commit is contained in:
parent
b615d1350d
commit
bbea58dabb
7 changed files with 206 additions and 167 deletions
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -586,6 +586,13 @@
|
|||
Keep the current tab in sync between the input and output
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox option-item">
|
||||
<label for="emptyInputPreserve">
|
||||
<input type="checkbox" option="emptyInputPreserve" id="emptyInputPreserve" checked>
|
||||
Include empty inputs in URL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button>
|
||||
|
|
|
@ -54,7 +54,8 @@ function main() {
|
|||
autoMagic: true,
|
||||
imagePreview: true,
|
||||
syncTabs: true,
|
||||
preserveCR: "entropy"
|
||||
preserveCR: "entropy",
|
||||
emptyInputPreserve: false
|
||||
};
|
||||
|
||||
document.removeEventListener("DOMContentLoaded", main, false);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import Utils from "../../core/Utils.mjs";
|
||||
import { toBase64 } from "../../core/lib/Base64.mjs";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -100,10 +101,10 @@ class ControlsWaiter {
|
|||
const includeRecipe = document.getElementById("save-link-recipe-checkbox").checked;
|
||||
const includeInput = document.getElementById("save-link-input-checkbox").checked;
|
||||
const saveLinkEl = document.getElementById("save-link");
|
||||
const saveLink = this.generateStateUrl(includeRecipe, includeInput, null, recipeConfig);
|
||||
|
||||
saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120));
|
||||
saveLinkEl.setAttribute("href", saveLink);
|
||||
this.generateStateUrl(includeRecipe, includeInput, recipeConfig).then((saveLink) => {
|
||||
saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120));
|
||||
saveLinkEl.setAttribute("href", saveLink);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -112,12 +113,11 @@ class ControlsWaiter {
|
|||
*
|
||||
* @param {boolean} includeRecipe - Whether to include the recipe in the URL.
|
||||
* @param {boolean} includeInput - Whether to include the input in the URL.
|
||||
* @param {string} input
|
||||
* @param {Object[]} [recipeConfig] - The recipe configuration object array.
|
||||
* @param {string} [baseURL] - The CyberChef URL, set to the current URL if not included
|
||||
* @returns {string}
|
||||
*/
|
||||
generateStateUrl(includeRecipe, includeInput, input, recipeConfig, baseURL) {
|
||||
async generateStateUrl(includeRecipe, includeInput, recipeConfig, baseURL) {
|
||||
recipeConfig = recipeConfig || this.app.getRecipeConfig();
|
||||
|
||||
const link = baseURL || window.location.protocol + "//" +
|
||||
|
@ -127,22 +127,31 @@ class ControlsWaiter {
|
|||
|
||||
includeRecipe = includeRecipe && (recipeConfig.length > 0);
|
||||
|
||||
// If we don't get passed an input, get it from the current URI
|
||||
if (input === null && includeInput) {
|
||||
const params = this.app.getURIParams();
|
||||
if (params.input) {
|
||||
includeInput = true;
|
||||
input = params.input;
|
||||
} else {
|
||||
includeInput = false;
|
||||
const params = [includeRecipe ? ["recipe", recipeStr] : undefined];
|
||||
|
||||
if (includeInput) {
|
||||
// getTabList() only returns visible tabs so we need to use getInputNums().
|
||||
const inputRange = (await this.manager.input.getInputNums()).inputNums;
|
||||
for (let i = 0; i < inputRange.length; i++) {
|
||||
const inputNum = parseInt(inputRange[i], 10);
|
||||
|
||||
const inputValue = toBase64(await this.manager.input.getInputValue(inputNum), "A-Za-z0-9-_");
|
||||
if (typeof inputValue !== "string" || inputValue.length > 2048 ||
|
||||
(!this.app.options.emptyInputPreserve && inputValue.length === 0)) {
|
||||
// Don't store other datatypes or strings that are too long (arbitrary size limit).
|
||||
continue;
|
||||
}
|
||||
|
||||
let inputIdentifier = inputNum.toString();
|
||||
const tabHeader = await this.manager.input.getTabName(inputNum);
|
||||
if (typeof tabHeader === "string" && tabHeader.length > 0) {
|
||||
inputIdentifier = "'" + toBase64(tabHeader, "A-Za-z0-9-_") + "'";
|
||||
}
|
||||
|
||||
params.push([`input[${inputIdentifier}]`, inputValue]);
|
||||
}
|
||||
}
|
||||
|
||||
const params = [
|
||||
includeRecipe ? ["recipe", recipeStr] : undefined,
|
||||
includeInput ? ["input", Utils.escapeHtml(input)] : undefined,
|
||||
];
|
||||
|
||||
const hash = params
|
||||
.filter(v => v)
|
||||
.map(([key, value]) => `${key}=${Utils.encodeURIFragment(value)}`)
|
||||
|
@ -344,17 +353,17 @@ class ControlsWaiter {
|
|||
e.preventDefault();
|
||||
|
||||
const reportBugInfo = document.getElementById("report-bug-info");
|
||||
const saveLink = this.generateStateUrl(true, true, null, null, "https://gchq.github.io/CyberChef/");
|
||||
this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/").then((saveLink) => {
|
||||
if (reportBugInfo) {
|
||||
reportBugInfo.innerHTML = `* Version: ${PKG_VERSION}
|
||||
* Compile time: ${COMPILE_TIME}
|
||||
* User-Agent:
|
||||
${navigator.userAgent}
|
||||
* [Link to reproduce](${saveLink})
|
||||
|
||||
if (reportBugInfo) {
|
||||
reportBugInfo.innerHTML = `* Version: ${PKG_VERSION}
|
||||
* Compile time: ${COMPILE_TIME}
|
||||
* User-Agent:
|
||||
${navigator.userAgent}
|
||||
* [Link to reproduce](${saveLink})
|
||||
|
||||
`;
|
||||
}
|
||||
`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import LoaderWorker from "worker-loader?inline=no-fallback!../workers/LoaderWorker.js";
|
||||
import InputWorker from "worker-loader?inline=no-fallback!../workers/InputWorker.mjs";
|
||||
import Utils, { debounce } from "../../core/Utils.mjs";
|
||||
import { toBase64 } from "../../core/lib/Base64.mjs";
|
||||
import { isImage } from "../../core/lib/FileType.mjs";
|
||||
|
||||
|
||||
|
@ -291,7 +290,7 @@ class InputWaiter {
|
|||
this.app.handleError(r.data);
|
||||
break;
|
||||
case "setUrl":
|
||||
this.setUrl(r.data);
|
||||
this.setUrl();
|
||||
break;
|
||||
case "inputSwitch":
|
||||
this.manager.output.inputSwitch(r.data);
|
||||
|
@ -306,6 +305,9 @@ class InputWaiter {
|
|||
case "fileLoaded":
|
||||
this.fileLoaded(r.data.inputNum);
|
||||
break;
|
||||
case "getTabName":
|
||||
this.callbacks[r.data.id](r.data);
|
||||
break;
|
||||
default:
|
||||
log.error(`Unknown action ${r.action}.`);
|
||||
}
|
||||
|
@ -363,14 +365,7 @@ class InputWaiter {
|
|||
inputData.input.count("\n") + 1 : null;
|
||||
this.setInputInfo(inputData.input.length, lines);
|
||||
|
||||
// Set URL to current input
|
||||
const inputStr = toBase64(inputData.input, "A-Za-z0-9+/");
|
||||
if (inputStr.length > 0 && inputStr.length <= 68267) {
|
||||
this.setUrl({
|
||||
includeInput: true,
|
||||
input: inputStr
|
||||
});
|
||||
}
|
||||
this.setUrl();
|
||||
|
||||
if (!silent) window.dispatchEvent(this.manager.statechange);
|
||||
} else {
|
||||
|
@ -527,15 +522,7 @@ class InputWaiter {
|
|||
* @param {boolean} [force=false] - If true, forces the value to be updated even if the type is different to the currently stored type
|
||||
*/
|
||||
updateInputValue(inputNum, value, force=false) {
|
||||
let includeInput = false;
|
||||
const recipeStr = toBase64(value, "A-Za-z0-9+/"); // B64 alphabet with no padding
|
||||
if (recipeStr.length > 0 && recipeStr.length <= 68267) {
|
||||
includeInput = true;
|
||||
}
|
||||
this.setUrl({
|
||||
includeInput: includeInput,
|
||||
input: recipeStr
|
||||
});
|
||||
this.setUrl();
|
||||
|
||||
// Value is either a string set by the input or an ArrayBuffer from a LoaderWorker,
|
||||
// so is safe to use typeof === "string"
|
||||
|
@ -1084,6 +1071,7 @@ class InputWaiter {
|
|||
this.setupInputWorker();
|
||||
this.manager.worker.setupChefWorker();
|
||||
this.addInput(true);
|
||||
this.manager.input.changeTab(1, true);
|
||||
this.bakeAll();
|
||||
}
|
||||
|
||||
|
@ -1095,7 +1083,7 @@ class InputWaiter {
|
|||
const inputNum = this.manager.tabs.getActiveInputTab();
|
||||
if (inputNum === -1) return;
|
||||
|
||||
this.manager.tabs.removeTabHeaderAlias(inputNum);
|
||||
this.manager.input.setTabName(inputNum, "");
|
||||
this.manager.highlighter.removeHighlights();
|
||||
getSelection().removeAllRanges();
|
||||
|
||||
|
@ -1234,7 +1222,6 @@ class InputWaiter {
|
|||
removeChefWorker: true
|
||||
}
|
||||
});
|
||||
this.manager.tabs.removeTabHeaderAlias(inputNum);
|
||||
|
||||
this.manager.output.removeTab(inputNum);
|
||||
}
|
||||
|
@ -1248,10 +1235,9 @@ class InputWaiter {
|
|||
if (!mouseEvent.target) {
|
||||
return;
|
||||
}
|
||||
const tabNumStr = mouseEvent.target.closest("button").parentElement.getAttribute("inputNum");
|
||||
if (tabNumStr) {
|
||||
const tabNum = parseInt(tabNumStr, 10);
|
||||
this.removeInput(tabNum);
|
||||
const tabNum = mouseEvent.target.closest("button").parentElement.getAttribute("inputNum");
|
||||
if (tabNum) {
|
||||
this.removeInput(parseInt(tabNum, 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1437,13 +1423,61 @@ class InputWaiter {
|
|||
|
||||
/**
|
||||
* Update the input URL to the new value
|
||||
*
|
||||
* @param {object} urlData - Object containing the URL data
|
||||
* @param {boolean} urlData.includeInput - If true, the input is included in the title
|
||||
* @param {string} urlData.input - The input data to be included
|
||||
*/
|
||||
setUrl(urlData) {
|
||||
this.app.updateTitle(urlData.includeInput, urlData.input, true);
|
||||
setUrl() {
|
||||
this.app.updateTitle(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the custom name of any tab
|
||||
*
|
||||
* @param {number} inputNum - The input number of the tab
|
||||
* @returns {string} - The tab's custom name or null if it has not been assigned one
|
||||
*/
|
||||
async getTabName(inputNum) {
|
||||
if (inputNum <= 0) {
|
||||
return null;
|
||||
}
|
||||
const tabName = (await new Promise(resolve => {
|
||||
this.queryTabName(r => {
|
||||
resolve(r);
|
||||
}, inputNum);
|
||||
})).data;
|
||||
return tabName === null ? "" : tabName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the inputWorker a message requesting the custom name of a given tab
|
||||
*
|
||||
* @param {object} callback - The callback to be executed after the worker finishes
|
||||
* @param {number} inputNum - The input number of the tab
|
||||
*/
|
||||
queryTabName(callback, inputNum) {
|
||||
const callbackId = this.callbackID++;
|
||||
this.callbacks[callbackId] = callback;
|
||||
this.inputWorker.postMessage({
|
||||
action: "getTabName",
|
||||
data: {
|
||||
id: callbackId,
|
||||
inputNum: inputNum
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the provided tab with a custo name
|
||||
*
|
||||
* @param {number} inputNum - The input number of the tab
|
||||
* @param {string} tabName - The new name for the tab
|
||||
*/
|
||||
setTabName(inputNum, tabName) {
|
||||
this.inputWorker.postMessage({
|
||||
action: "setTabName",
|
||||
data: {
|
||||
inputNum: inputNum,
|
||||
tabName: tabName
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1461,9 +1495,6 @@ class InputWaiter {
|
|||
let renameContents = targetElement.textContent;
|
||||
const renameContentsColon = renameContents.indexOf(":");
|
||||
|
||||
// Calling 'getInputValue()' might take a long time for large datasets,
|
||||
// it could be beneficial to modify the API to allow for querying whether
|
||||
// there is any input rather than getting the full string just to access its length.
|
||||
const inputLength = (await this.getInputValue(this.manager.tabs.getActiveInputTab())).length;
|
||||
|
||||
// Remove the data from the renaming section
|
||||
|
@ -1499,20 +1530,25 @@ class InputWaiter {
|
|||
if (activeInputTabNum === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeInputTabElement = this.manager.tabs.getTabItem(activeInputTabNum, "input");
|
||||
if (activeInputTabElement == null || activeInputTabElement.children.size < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tabContent = activeInputTabElement.children[0];
|
||||
const tabHeader = tabContent.children[0].children[0].value;
|
||||
const inputContents = await this.getInputValue(activeInputTabNum);
|
||||
|
||||
if (tabHeader.length === 0)
|
||||
this.manager.tabs.removeTabHeaderAlias(activeInputTabNum, false);
|
||||
this.manager.input.setTabName(activeInputTabNum, "");
|
||||
else
|
||||
this.manager.tabs.addTabHeaderAlias(activeInputTabNum, tabHeader);
|
||||
this.manager.input.setTabName(activeInputTabNum, tabHeader);
|
||||
|
||||
this.manager.tabs.updateInputTabHeader(activeInputTabNum, inputContents);
|
||||
this.manager.tabs.updateOutputTabHeader(activeInputTabNum, inputContents);
|
||||
|
||||
this.manager.input.setUrl();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ class TabWaiter {
|
|||
constructor(app, manager) {
|
||||
this.app = app;
|
||||
this.manager = manager;
|
||||
this.tabHeaderAliases = []; // Mapping custom tab headers to indexes/numbers.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,6 +150,8 @@ class TabWaiter {
|
|||
const newTab = document.createElement("li");
|
||||
newTab.setAttribute("inputNum", inputNum.toString());
|
||||
|
||||
newTab.setAttribute("tabHeader", "");
|
||||
|
||||
if (active) newTab.classList.add(`active-${io}-tab`);
|
||||
|
||||
const newTabContent = document.createElement("div");
|
||||
|
@ -178,6 +179,8 @@ class TabWaiter {
|
|||
newTab.appendChild(newTabButton);
|
||||
}
|
||||
|
||||
this.manager.input.setUrl();
|
||||
|
||||
return newTab;
|
||||
}
|
||||
|
||||
|
@ -353,11 +356,9 @@ class TabWaiter {
|
|||
const tab = this.getTabItem(inputNum, io);
|
||||
if (tab == null) return;
|
||||
|
||||
const customHeaderData = this.getTabHeaderAlias(inputNum);
|
||||
const customHeaderData = await this.manager.input.getTabName(inputNum);
|
||||
|
||||
// When 'customHeaderData === `Tab ${inputNum}`' is true, it's usually due to
|
||||
// a user having opened the rename textbox but then closing it without change.
|
||||
const isStandardHeader = customHeaderData === null || customHeaderData === `Tab ${inputNum}`;
|
||||
const isStandardHeader = customHeaderData.length === 0 || customHeaderData === `Tab ${inputNum}`;
|
||||
|
||||
let headerData = isStandardHeader ? `Tab ${inputNum}` : customHeaderData;
|
||||
const dataIsFile = data instanceof ArrayBuffer;
|
||||
|
@ -375,9 +376,11 @@ class TabWaiter {
|
|||
|
||||
headerData += `: ${dataPreview}`;
|
||||
}
|
||||
|
||||
tab.firstElementChild.innerText = headerData;
|
||||
if (!isStandardHeader && !includeData)
|
||||
if (!isStandardHeader && !includeData) {
|
||||
tab.firstElementChild.innerText = `'${headerData}'`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -441,63 +444,6 @@ class TabWaiter {
|
|||
updateOutputTabProgress(inputNum, progress, total) {
|
||||
this.updateTabProgress(inputNum, progress, total, "output");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias between a custom tab header and a tab number so that
|
||||
* mapping between the two is possible if DOM element is removed.
|
||||
*
|
||||
* @param {number} tabNum - The index of the tab being aliased
|
||||
* @param {string} tabHeader - The custom tab header
|
||||
*/
|
||||
addTabHeaderAlias(tabNum, tabHeader) {
|
||||
// First, we try to overwrite an existing alias.
|
||||
for (let i = 0; i < this.tabHeaderAliases.length; i++) {
|
||||
if (this.tabHeaderAliases.at(i).tabNumber === tabNum) {
|
||||
this.tabHeaderAliases.at(i).customHeader = tabHeader;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.tabHeaderAliases.push({tabNumber: tabNum, customHeader: tabHeader});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously-assigned header alias.
|
||||
*
|
||||
* @param {number} tabNum - The index of the tab that should be removed.
|
||||
* @param {boolean} shouldThrow - A boolean representing whether the function should throw an exception or return silently if it cannot locate the tab header.
|
||||
*/
|
||||
removeTabHeaderAlias(tabNum, shouldThrow) {
|
||||
for (let i = 0; i < this.tabHeaderAliases.length; i++) {
|
||||
|
||||
if (this.tabHeaderAliases.at(i).tabNumber === tabNum) {
|
||||
this.tabHeaderAliases.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
if (shouldThrow)
|
||||
throw `Unable to locate header alias at tab index ${tabNum.toString()}.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the custom header for a given tab.
|
||||
*
|
||||
* @param {number} tabNum - The index of the tab whose alias should be retrieved.
|
||||
* @param {boolean} shouldThrow - Whether the function should throw an exception (instead of returning null) in the event of it being unable to locate the tab.
|
||||
* @returns {string} customHeader - The custom header for the requested tab.
|
||||
*/
|
||||
getTabHeaderAlias(tabNum, shouldThrow) {
|
||||
for (let i = 0; i < this.tabHeaderAliases.length; i++) {
|
||||
|
||||
if (this.tabHeaderAliases.at(i).tabNumber === tabNum)
|
||||
return this.tabHeaderAliases.at(i).customHeader;
|
||||
|
||||
}
|
||||
if (shouldThrow)
|
||||
throw `Unable to locate header alias at tab index ${tabNum.toString()}.`;
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default TabWaiter;
|
||||
|
|
|
@ -113,6 +113,12 @@ self.addEventListener("message", function(e) {
|
|||
case "getInputNums":
|
||||
self.getInputNums(r.data);
|
||||
break;
|
||||
case "getTabName":
|
||||
self.getTabName(r.data);
|
||||
break;
|
||||
case "setTabName":
|
||||
self.setTabName(r.data.inputNum, r.data.tabName);
|
||||
break;
|
||||
default:
|
||||
log.error(`Unknown action '${r.action}'.`);
|
||||
}
|
||||
|
@ -413,14 +419,17 @@ self.getNearbyNums = function(inputNum, direction) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Gets the data to display in the tab header for an input, and
|
||||
* posts it back to the inputWaiter
|
||||
* Gets the data to display in the tab header for an input and
|
||||
* posts it back to the inputWaiter.
|
||||
*
|
||||
* @param {number} inputNum - The inputNum of the tab header
|
||||
* @param {number} inputNum - The inputNum of the tab header.
|
||||
*/
|
||||
self.updateTabHeader = function(inputNum) {
|
||||
const input = self.getInputObj(inputNum);
|
||||
if (input === null || input === undefined) return;
|
||||
if (input === null || input === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let inputData = input.data;
|
||||
if (typeof inputData !== "string") {
|
||||
inputData = input.data.name;
|
||||
|
@ -1079,3 +1088,33 @@ self.inputSwitch = function(switchData) {
|
|||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the custom name of a tab, returning null if one has not been set
|
||||
*
|
||||
* @param {object} inputData
|
||||
* @param {number} inputData.inputNum - The input number of the tab
|
||||
* @param {number} inputData.id - The callback ID for the callvack to run when returning to the inputWaiter
|
||||
*/
|
||||
self.getTabName = function(inputData) {
|
||||
const { inputNum, id } = inputData;
|
||||
const inputName = self.inputs[inputNum].inputName;
|
||||
const tabName = typeof inputName !== "string" ? null : inputName;
|
||||
self.postMessage({
|
||||
action: "getTabName",
|
||||
data: {
|
||||
data: tabName,
|
||||
id: id
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the custom name of a tab, overwriting any previously-assigned one
|
||||
*
|
||||
* @param {number} inputNum - The input number of the tab
|
||||
* @param {string} tabName - The custom name that should be assigned to the tab
|
||||
*/
|
||||
self.setTabName = function(inputNum, tabName) {
|
||||
self.inputs[inputNum].inputName = tabName;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue