diff --git a/src/core/Operation.mjs b/src/core/Operation.mjs index 32ecff07ca..d9ef2a959f 100755 --- a/src/core/Operation.mjs +++ b/src/core/Operation.mjs @@ -24,6 +24,7 @@ class Operation { this._disabled = false; this._flowControl = false; this._manualBake = false; + this._inputSwappable = false; this._ingList = []; // Public fields @@ -316,6 +317,25 @@ class Operation { this._manualBake = !!value; } + /** + * Returns true if this Operation has swappable input values. + * + * @returns {boolean} + */ + get inputSwappable() { + return this._inputSwappable; + } + + + /** + * Set whether this Operation shoud show swap input icon. + * + * @param {boolean} value + */ + set inputSwappable(value) { + this._inputSwappable = !!value; + } + } export default Operation; diff --git a/src/core/config/scripts/generateConfig.mjs b/src/core/config/scripts/generateConfig.mjs index 8ef906e617..528c06d4c3 100644 --- a/src/core/config/scripts/generateConfig.mjs +++ b/src/core/config/scripts/generateConfig.mjs @@ -35,14 +35,15 @@ for (const opObj in Ops) { const op = new Ops[opObj](); operationConfig[op.name] = { - module: op.module, - description: op.description, - infoURL: op.infoURL, - inputType: op.inputType, - outputType: op.presentType, - flowControl: op.flowControl, - manualBake: op.manualBake, - args: op.args + module: op.module, + description: op.description, + infoURL: op.infoURL, + inputType: op.inputType, + outputType: op.presentType, + flowControl: op.flowControl, + inputSwappable: op.inputSwappable, + manualBake: op.manualBake, + args: op.args }; if ("patterns" in op) { diff --git a/src/core/config/scripts/newOperation.mjs b/src/core/config/scripts/newOperation.mjs index 03753faeb9..938e1bc0de 100644 --- a/src/core/config/scripts/newOperation.mjs +++ b/src/core/config/scripts/newOperation.mjs @@ -77,6 +77,14 @@ If your operation does not rely on a library, just leave this blank and it will required: true, message: `The output type should be one of: ${ioTypes.join(", ")}.` }, + inputSwappable: { + description: "Input swappable option shows an icon that can swap input values of two ingredients.", + example: "true/false", + prompt: "Show input swaping", + type: "boolean", + default: "false", + message: "Enter true or false to specify if input spawing should be shown." + }, highlight: { description: "If your operation does not change the length of the input in any way, we can enable highlighting. If it does change the length in a predictable way, we may still be able to enable highlighting and calculate the correct offsets. If this is not possible, we will disable highlighting for this operation.", example: "true/false", @@ -150,6 +158,7 @@ class ${moduleName} extends Operation { this.infoURL = "${result.infoURL}"; this.inputType = "${result.inputType}"; this.outputType = "${result.outputType}"; + this.inputSwappable = "${result.inputSwappable}"; this.args = [ /* Example arguments. See the project wiki for full details. { diff --git a/src/core/operations/ConvertArea.mjs b/src/core/operations/ConvertArea.mjs index 4cce31b139..1e6d5bc75e 100644 --- a/src/core/operations/ConvertArea.mjs +++ b/src/core/operations/ConvertArea.mjs @@ -23,6 +23,7 @@ class ConvertArea extends Operation { this.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(area)"; this.inputType = "BigNumber"; this.outputType = "BigNumber"; + this.inputSwappable = true; this.args = [ { "name": "Input units", diff --git a/src/core/operations/ConvertDataUnits.mjs b/src/core/operations/ConvertDataUnits.mjs index baed900407..a5859102ef 100644 --- a/src/core/operations/ConvertDataUnits.mjs +++ b/src/core/operations/ConvertDataUnits.mjs @@ -23,6 +23,7 @@ class ConvertDataUnits extends Operation { this.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(data)"; this.inputType = "BigNumber"; this.outputType = "BigNumber"; + this.inputSwappable = true; this.args = [ { "name": "Input units", diff --git a/src/core/operations/ConvertDistance.mjs b/src/core/operations/ConvertDistance.mjs index 1a5fc8afda..8d7f5cc239 100644 --- a/src/core/operations/ConvertDistance.mjs +++ b/src/core/operations/ConvertDistance.mjs @@ -23,6 +23,7 @@ class ConvertDistance extends Operation { this.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(length)"; this.inputType = "BigNumber"; this.outputType = "BigNumber"; + this.inputSwappable = true; this.args = [ { "name": "Input units", diff --git a/src/core/operations/ConvertMass.mjs b/src/core/operations/ConvertMass.mjs index 712884ed93..573a7e419c 100644 --- a/src/core/operations/ConvertMass.mjs +++ b/src/core/operations/ConvertMass.mjs @@ -23,6 +23,7 @@ class ConvertMass extends Operation { this.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(mass)"; this.inputType = "BigNumber"; this.outputType = "BigNumber"; + this.inputSwappable = true; this.args = [ { "name": "Input units", diff --git a/src/core/operations/ConvertSpeed.mjs b/src/core/operations/ConvertSpeed.mjs index 7fc6718dbf..4799a3a3f7 100644 --- a/src/core/operations/ConvertSpeed.mjs +++ b/src/core/operations/ConvertSpeed.mjs @@ -23,6 +23,7 @@ class ConvertSpeed extends Operation { this.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(speed)"; this.inputType = "BigNumber"; this.outputType = "BigNumber"; + this.inputSwappable = true; this.args = [ { "name": "Input units", diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index fe075c486f..9145caa09c 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -21,15 +21,16 @@ class HTMLOperation { * @param {Manager} manager - The CyberChef event manager. */ constructor(name, config, app, manager) { - this.app = app; - this.manager = manager; + this.app = app; + this.manager = manager; - this.name = name; - this.description = config.description; - this.infoURL = config.infoURL; - this.manualBake = config.manualBake || false; - this.config = config; - this.ingList = []; + this.name = name; + this.description = config.description; + this.infoURL = config.infoURL; + this.manualBake = config.manualBake || false; + this.inputSwappable = config.inputSwappable || false; + this.config = config; + this.ingList = []; for (let i = 0; i < config.args.length; i++) { const ing = new HTMLIngredient(config.args[i], this.app, this.manager); @@ -77,6 +78,9 @@ class HTMLOperation { for (let i = 0; i < this.ingList.length; i++) { html += this.ingList[i].toHtml(); + if (this.inputSwappable && i === 0) { + html += "
swap_horiz
"; + } } html += ` diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index cb579721f9..5db822dbb1 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -137,6 +137,7 @@ class Manager { this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe); this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe); this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe); + this.addDynamicListener(".swap-icon", "click", this.recipe.swapInputClick, this.recipe); this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe); this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe); this.addDynamicListener("#rec-list .dropdown-menu.toggle-dropdown a", "click", this.recipe.dropdownToggleClick, this.recipe); diff --git a/src/web/stylesheets/components/_operation.css b/src/web/stylesheets/components/_operation.css index a4255fc33a..affaa03d35 100755 --- a/src/web/stylesheets/components/_operation.css +++ b/src/web/stylesheets/components/_operation.css @@ -240,6 +240,11 @@ div.toggle-string { cursor: pointer; } +.swap-icon { + margin-top: 1.75rem; + cursor: pointer; +} + .disable-icon { color: var(--disable-icon-colour); } diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index d198098b73..65a96016dd 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -261,6 +261,23 @@ class RecipeWaiter { window.dispatchEvent(this.manager.statechange); } + /** + * Handler for swap input click event. + * Swaps values of two inputs. + * + * @fires Manager#statechange + * @param {event} e + */ + swapInputClick(e) { + const si = e.target; + const ings = si.parentNode.parentNode.querySelectorAll(".arg"); + + if (ings.length === 2) { + const left = ings[0], right = ings[1]; + [left.value, right.value] = [right.value, left.value]; + this.ingChange(); + } + } /** * Handler for operation doubleclick events.