diff --git a/src/web/App.mjs b/src/web/App.mjs index c5202be8..2710b045 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -315,7 +315,7 @@ class App { this.columnSplitter = Split(["#operations", "#recipe", "#IO"], { sizes: [20, 30, 50], - minSize: minimise ? [0, 0, 0] : [240, 310, 450], + minSize: minimise ? [0, 0, 0] : [20, 30, 50], gutterSize: 4, expandToMin: true, onDrag: debounce(function() { @@ -326,7 +326,7 @@ class App { this.ioSplitter = Split(["#input", "#output"], { direction: "vertical", gutterSize: 4, - minSize: minimise ? [0, 0] : [100, 100] + minSize: minimise ? [0, 0] : [50, 50] }); this.adjustComponentSizes(); @@ -341,7 +341,6 @@ class App { this.columnSplitter = Split(["#operations", "#recipe", "#IO"], { sizes: [100, 100, 100], - minSize: [0, 0, 0], gutterSize: 0, expandToMin: true, }); @@ -649,7 +648,7 @@ class App { /** - * Resets the splitter positions to default. + * Resets the splitter positions to default for desktop UI. */ resetLayout() { this.columnSplitter.setSizes([20, 30, 50]); @@ -657,6 +656,7 @@ class App { this.adjustComponentSizes(); } + /** * Adjust components to fit their containers. */ @@ -857,13 +857,13 @@ class App { /** - * Set element visibility + * Update element visibility * * @param {HTMLElement} elm * @param {boolean} isVisible * */ - setVisibility( elm, isVisible ){ + updateVisibility( elm, isVisible ){ if ( isVisible ) { if ( elm.classList.contains("hidden")) { elm.classList.remove("hidden"); @@ -876,10 +876,6 @@ class App { } /** - * A collection of function calls that need to fire on - * window resizing when the window inner width >= the - * breakpoint - * * @param {boolean} minimise */ setDesktopUI(minimise){ @@ -893,11 +889,6 @@ class App { this.manager.recipe.clearAllSelectedClasses(); } - /** - * A collection of function calls that need to fire on - * window resizing when the window inner width < the - * breakpoint - */ setMobileUI(){ this.setMobileLayout(); // repopulate to disable popovers and drag events diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 1be83fbc..b1720b66 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -141,6 +141,7 @@ class Manager { document.getElementById("load-button").addEventListener("click", this.controls.loadButtonClick.bind(this.controls)); document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls)); this.addMultiEventListeners("#save-texts textarea", "keyup paste", this.controls.saveTextChange, this.controls); + this.addDynamicListener(".btn-maximise", "click", this.controls.handlePaneMaximising, this.controls); // Operations this.addMultiEventListener("#search", "keyup paste search click", this.ops.searchOperations, this.ops); @@ -199,7 +200,7 @@ class Manager { document.getElementById("save-all-to-file").addEventListener("click", this.output.saveAllClick.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("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)); this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output); this.addDynamicListener("#output-tabs-wrapper #output-tabs li .output-tab-content", "click", this.output.changeTabClick, this.output); diff --git a/src/web/TODO.md b/src/web/TODO.md index d2022927..bcfa2019 100644 --- a/src/web/TODO.md +++ b/src/web/TODO.md @@ -4,13 +4,13 @@ --- #### Mobile UI ( on real device ): -- How to add operations to favourites since drag and drop is now disabled on mobile ( maybe a star icon...? ) -- Recipe list on mobile panel is too small to comfortably scroll and change order of recipes - - test *thoroughly* with keyboard popping up because that messes with view-heights on mobile probably and might make it a very frustrating experience - test drag and drop etc. Regular mobile events / UX - view-heights not correct due to variable taskbar on mobile devices +- loading gears on mobile ( some ingredient labels 'shine through' and the cancel button isnt visible ) +- need long press checks on mobile to add favourites and switch ingredient order + ### Desktop UI: ### General UI: - fix up key / tab events so UI can be navigated comfortably with keys ( inc. visual focus feedback ). Probably a lot of work though diff --git a/src/web/html/index.html b/src/web/html/index.html index 947ea79f..eab663d1 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -198,7 +198,7 @@ data-help-title="Operations list" data-help="

The Operations list contains all the operations in CyberChef arranged into categories. Some operations may be present in multiple categories. You can search for operations using the search box.

To use an operation, either double click it, or drag it into the Recipe pane. You will then be able to configure its arguments (or 'Ingredients' in CyberChef terminology).

"> Operations - + @@ -236,10 +236,13 @@ + -
+
+ - diff --git a/src/web/stylesheets/components/_controls.css b/src/web/stylesheets/components/_controls.css index 3e2b1150..914aea5c 100644 --- a/src/web/stylesheets/components/_controls.css +++ b/src/web/stylesheets/components/_controls.css @@ -52,8 +52,8 @@ margin: 0; } -.output-maximised .hide-on-maximised-output { - display: none !important; +.top-zindex { + z-index: 200; } diff --git a/src/web/stylesheets/components/_recipe.css b/src/web/stylesheets/components/_recipe.css index 2dd0ab18..a8d1d910 100644 --- a/src/web/stylesheets/components/_recipe.css +++ b/src/web/stylesheets/components/_recipe.css @@ -8,6 +8,7 @@ #recipe { position: relative; + background-color: var(--primary-background-colour); } #rec-list { diff --git a/src/web/stylesheets/components/io/_io.css b/src/web/stylesheets/components/io/_io.css index 4374097b..073a8dac 100755 --- a/src/web/stylesheets/components/io/_io.css +++ b/src/web/stylesheets/components/io/_io.css @@ -6,6 +6,11 @@ * @license Apache-2.0 */ +#input, +#output { + background-color: var(--primary-background-colour); +} + #input-text, #output-text { position: relative; diff --git a/src/web/stylesheets/components/operations/_operations.css b/src/web/stylesheets/components/operations/_operations.css index bfc9956b..afecbc38 100644 --- a/src/web/stylesheets/components/operations/_operations.css +++ b/src/web/stylesheets/components/operations/_operations.css @@ -5,6 +5,10 @@ * can ( should ) be made into a reusable, generic component */ +#operations { + background-color: var(--primary-background-colour); +} + #operations-wrapper { position: relative; } diff --git a/src/web/stylesheets/layout/_structure.css b/src/web/stylesheets/layout/_structure.css index dbd028f5..0127bc12 100755 --- a/src/web/stylesheets/layout/_structure.css +++ b/src/web/stylesheets/layout/_structure.css @@ -24,6 +24,7 @@ #content-wrapper { top: 0; bottom: 0; + overflow: hidden; } #workspace-wrapper { @@ -45,6 +46,22 @@ padding-bottom: var(--controls-height); } +#recipe.maximised-pane, +#input.maximised-pane, +#output.maximised-pane { + position: fixed; + min-height: calc( 100vh - var(--banner-height )); + height: auto; + top: var(--banner-height); + left: 0; + right: 0; + bottom: 0; +} + +#recipe.maximised-pane #controls { + display: none; +} + @media only screen and ( min-width: 768px ) { #IO { padding-bottom: 0; diff --git a/src/web/waiters/ControlsWaiter.mjs b/src/web/waiters/ControlsWaiter.mjs index a86cce79..740be758 100755 --- a/src/web/waiters/ControlsWaiter.mjs +++ b/src/web/waiters/ControlsWaiter.mjs @@ -418,6 +418,92 @@ ${navigator.userAgent} bakeButton.classList.add("btn-success"); } } + + + /** + * Handle the maximising and resetting to default state of + * panels. + * + * On mobile UI, #recipe, #input and #output can be maximised, + * on desktop UI it's available only for #output + * + * @param {Event} e + */ + handlePaneMaximising(e){ + // the event target btn can be one of ( currently ) 3 'maximiser' buttons + const btn = e.target.classList.contains("btn-maximise") ? e.target : e.target.parentNode; + // find the parent ( target ) pane to be maximised that belongs to the btn + const pane = this.resolveMaximiserParentPane( btn.id ) + + if (btn.getAttribute("data-original-title") === "Maximise pane") { + this.maximisePane(btn,pane); + } else { + this.resetPane(btn, pane); + } + } + + /** + * Get the parent pane of the 'maximise' button / icon that was + * clicked through the buttons' ID + * + * @param {string} id + */ + resolveMaximiserParentPane(id){ + switch(id) { + case "maximise-recipe": + return document.getElementById("recipe"); + case "maximise-input": + return document.getElementById("input"); + case "maximise-output": + return document.getElementById("output"); + } + } + + /** + * Set the pane from default state to Maximised + * + * @param {HTMLElement} btn + * @param {HTMLElement} pane + */ + maximisePane(btn, pane){ + this.togglePane(pane,true); + this.toggleIcon(btn, true); + } + + /** + * Reset the pane from Maximised state to default + * + * @param {HTMLElement} btn + * @param {HTMLElement} pane + */ + resetPane(btn, pane) { + this.togglePane(pane, false); + this.toggleIcon(btn, false); + } + + /** + * Toggle the pane to or from maximised size, + * based on the 'isMaximised' flag + * + * @param {HTMLElement} pane + * @param {boolean} isMaximised + */ + togglePane(pane, isMaximised) { + isMaximised ? pane.classList.add("top-zindex") : pane.classList.remove("top-zindex"); + isMaximised ? pane.classList.add("maximised-pane") : pane.classList.remove("maximised-pane"); + } + + /** + * Toggle the 'maximise' icon and attribute text based on + * the 'isMaximised' flag + * + * @param {HTMLElement} btn + * @param {boolean} isMaximised + */ + toggleIcon(btn, isMaximised ) { + btn.querySelector("i").innerHTML = isMaximised ? "fullscreen_exit" : "fullscreen"; + $(btn).attr("data-original-title", isMaximised ? "Reset pane" : "Maximise pane"); + } } export default ControlsWaiter; diff --git a/src/web/waiters/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs index 89ec221e..471b781d 100755 --- a/src/web/waiters/OperationsWaiter.mjs +++ b/src/web/waiters/OperationsWaiter.mjs @@ -45,7 +45,7 @@ class OperationsWaiter { this.openOperationsDropdown(); if ( e.target.value.length !== 0 ){ - this.app.setVisibility(searchResults, true ); + this.app.updateVisibility(searchResults, true ); } } @@ -329,8 +329,8 @@ class OperationsWaiter { const closeOperationsDropdown = document.getElementById("close-operations-dropdown"); const categories = document.getElementById("categories"); - this.app.setVisibility(categories, true); - this.app.setVisibility(closeOperationsDropdown, true); + this.app.updateVisibility(categories, true); + this.app.updateVisibility(closeOperationsDropdown, true); } @@ -346,9 +346,9 @@ class OperationsWaiter { search.value = ''; } - this.app.setVisibility(document.getElementById( "categories"), false ); - this.app.setVisibility(document.getElementById( "search-results"), false ); - this.app.setVisibility(document.getElementById("close-operations-dropdown"), false ); + this.app.updateVisibility(document.getElementById( "categories"), false ); + this.app.updateVisibility(document.getElementById( "search-results"), false ); + this.app.updateVisibility(document.getElementById("close-operations-dropdown"), false ); } /** diff --git a/src/web/waiters/OutputWaiter.mjs b/src/web/waiters/OutputWaiter.mjs index dae27f3e..a210dca3 100755 --- a/src/web/waiters/OutputWaiter.mjs +++ b/src/web/waiters/OutputWaiter.mjs @@ -1404,26 +1404,29 @@ class OutputWaiter { * Handler for maximise output click events. * Resizes the output frame to be as large as possible, or restores it to its original size. */ - maximiseOutputClick(e) { - const el = e.target.id === "maximise-output" ? e.target : e.target.parentNode; + // maximiseOutputClick(e) { + // const el = e.target.id === "maximise-output" ? e.target : e.target.parentNode; + // + // if (el.getAttribute("data-original-title").indexOf("Maximise") === 0) { + // document.body.classList.add("output-maximised"); + // this.app.initialiseSplitter(true); + // this.app.columnSplitter.collapse(0); + // this.app.columnSplitter.collapse(1); + // this.app.ioSplitter.collapse(0); + // + // $(el).attr("data-original-title", "Restore output pane"); + // el.querySelector("i").innerHTML = "fullscreen_exit"; + // } else { + // document.body.classList.remove("output-maximised"); + // $(el).attr("data-original-title", "Maximise output pane"); + // el.querySelector("i").innerHTML = "fullscreen"; + // this.app.initialiseSplitter(false); + // // if ( window.innerWidth >= this.app.breakpoint ){ + // // this.app.resetLayout(); + // // } + // } + // } - if (el.getAttribute("data-original-title").indexOf("Maximise") === 0) { - document.body.classList.add("output-maximised"); - this.app.initialiseSplitter(true); - this.app.columnSplitter.collapse(0); - this.app.columnSplitter.collapse(1); - this.app.ioSplitter.collapse(0); - - $(el).attr("data-original-title", "Restore output pane"); - el.querySelector("i").innerHTML = "fullscreen_exit"; - } else { - document.body.classList.remove("output-maximised"); - $(el).attr("data-original-title", "Maximise output pane"); - el.querySelector("i").innerHTML = "fullscreen"; - this.app.initialiseSplitter(false); - this.app.resetLayout(); - } - } /** * Handler for find tab button clicked diff --git a/src/web/waiters/WindowWaiter.mjs b/src/web/waiters/WindowWaiter.mjs index 26354cb0..45e40182 100755 --- a/src/web/waiters/WindowWaiter.mjs +++ b/src/web/waiters/WindowWaiter.mjs @@ -27,7 +27,6 @@ class WindowWaiter { * continuous resetting). */ windowResize() { - // @TODO: maybe a debounce is desirable although generally people won't be resizing like crazy.. I think if ( window.innerWidth >= this.app.breakpoint ) { this.app.setDesktopUI(false); } else {