diff --git a/README.md b/README.md index 18e535ca52..82b80d86b4 100755 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ There are four main areas in CyberChef: 1. The **input** box in the top right, where you can paste, type or drag the text or file you want to operate on. 2. The **output** box in the bottom right, where the outcome of your processing will be displayed. - 3. The **operations** list on the far left, where you can find all the operations that CyberChef is capable of in categorised lists, or by searching. + 3. The **operations** list on the far left ( or in the dropdown at the top on mobile ), where you can find all the operations that CyberChef is capable of in categorised lists, or by searching. 4. The **recipe** area in the middle, where you can drag the operations that you want to use and specify arguments and options. You can use as many operations as you like in simple or complex ways. Some examples are as follows: @@ -49,6 +49,7 @@ You can use as many operations as you like in simple or complex ways. Some examp - Drag and drop - Operations can be dragged in and out of the recipe list, or reorganised. - Files up to 2GB can be dragged over the input box to load them directly into the browser. + - On mobile devices, double-click the operations to add them to the recipe list. - Auto Bake - Whenever you modify the input or the recipe, CyberChef will automatically "bake" for you and produce the output immediately. - This can be turned off and operated manually if it is affecting performance (if the input is very large, for instance). diff --git a/src/web/App.mjs b/src/web/App.mjs index f3c9ae25bc..0e119ec2b3 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -298,7 +298,7 @@ class App { * @param {boolean} [minimise=false] - Set this flag if attempting to minimise frames to 0 width */ initialiseUI() { - if ( window.innerWidth < this.breakpoint ){ + if (window.innerWidth < this.breakpoint) { this.setMobileUI(); } else { this.setDesktopUI(false); @@ -308,7 +308,7 @@ class App { /** * Set desktop splitters */ - setDesktopSplitter(minimise){ + setDesktopSplitter(minimise) { if (this.columnSplitter) this.columnSplitter.destroy(); if (this.ioSplitter) this.ioSplitter.destroy(); @@ -864,16 +864,16 @@ class App { * @param {boolean} isVisible * */ - updateVisibility( elm, isVisible ){ - isVisible ? elm.classList.remove("hidden") : elm.classList.add("hidden"); - }h + updateVisibility(elm, isVisible) { + return isVisible ? elm.classList.remove("hidden") : elm.classList.add("hidden"); + } /** * Set desktop UI ( on init and on window resize events ) * * @param {boolean} minimise */ - setDesktopUI(minimise){ + setDesktopUI(minimise) { $("[data-toggle=tooltip]").tooltip("enable"); this.setDesktopSplitter(minimise); this.adjustComponentSizes(); @@ -884,7 +884,7 @@ class App { /** * Set mobile UI ( on init and on window resize events ) */ - setMobileUI(){ + setMobileUI() { $("[data-toggle=tooltip]").tooltip("disable"); this.setMobileSplitter(); this.assignAvailableHeight(); @@ -901,7 +901,7 @@ class App { * Be mindful to update these fixed numbers accordingly in the stylesheets * ( themes/_structure ) if you make changes to those elements' height. */ - assignAvailableHeight(){ + assignAvailableHeight() { const bannerHeight = 40; const controlsHeight = 50; const operationsHeight = 80; @@ -909,7 +909,7 @@ class App { const remainingSpace = window.innerHeight - (bannerHeight+controlsHeight+operationsHeight - 1); // - 1 is accounting for a border // equally divide among recipe, input and output - ["recipe", "input", "output"].forEach(( div ) => { + ["recipe", "input", "output"].forEach((div) => { document.getElementById(div).style.height = `${remainingSpace/3}px`; }); diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index 6dfa2c44d9..481ad36465 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -64,17 +64,17 @@ class HTMLOperation { if (removeIcon) { html += "delete"; - } else if (!removeIcon && window.innerWidth < this.app.breakpoint ){ + } else if (!removeIcon && window.innerWidth < this.app.breakpoint) { html += "check"; } const favourites = JSON.parse(localStorage.favourites); const isFavourite = favourites.includes(this.name); - if ( window.innerWidth < this.app.breakpoint ){ + if (window.innerWidth < this.app.breakpoint) { html += ` ${isFavourite ? "star" : "star_outline"} - ` + `; } html += ""; diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 5f0edd3851..d088a0c3d5 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -142,9 +142,9 @@ class Manager { document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls)); this.addMultiEventListeners("#save-texts textarea", "keyup paste", this.controls.saveTextChange, this.controls); // A note for the Maximise Controls listeners below: click events via addDynamicListener don't properly bubble and the hit box to maximise is unacceptably tiny, hence this solution - document.getElementById("maximise-recipe").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls)) - document.getElementById("maximise-input").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls)) - document.getElementById("maximise-output").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls)) + document.getElementById("maximise-recipe").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls)); + document.getElementById("maximise-input").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls)); + document.getElementById("maximise-output").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls)); // Operations this.addMultiEventListener("#search", "keyup paste search click", this.ops.searchOperations, this.ops); diff --git a/src/web/TODO.md b/src/web/TODO.md index 1364d5fe75..3a9528f854 100644 --- a/src/web/TODO.md +++ b/src/web/TODO.md @@ -4,17 +4,14 @@ --- #### Mobile UI ( on real device ): -- shannon entropy thingies - maybe a bit annoying that the fav cat opens whenever you add a new fav via icon-fav-click on mobile -- backspace on fs view should close max view. Keep making the same mistake and navigating away when for instance recipe is expanded and double click the window to fs > resolve. Reset layout -- remove tabs on window resizing large to small? -- drag and drop from op to rec on desktop only working once +- backspace on fs view should close max view. Keep making the same mistake and navigating away when for instance recipe + is expanded and double click the window to fs > resolve. Reset layout ### JS: - `core/Recipe.mjs`, `core/lib/Magic.js` return imports to original ### Misc: - Gruntfile revert dev config -- Update README instructions per Mobile UI - delete this file when done :) diff --git a/src/web/utils/statusBar.mjs b/src/web/utils/statusBar.mjs index 5577c1782b..0abd1ab716 100644 --- a/src/web/utils/statusBar.mjs +++ b/src/web/utils/statusBar.mjs @@ -292,7 +292,7 @@ class StatusBarPanel { this.dom.querySelectorAll(".cm-status-bar-select-scroll").forEach( el => { - el.style.maxHeight = window.innerWidth >= 768 ? (viewHeight - 50) + "px" : '250px'; + el.style.maxHeight = window.innerWidth >= 768 ? (viewHeight - 50) + "px" : "250px"; } ); } @@ -476,7 +476,7 @@ function makePanel(opts) { sbPanel.updateSelection(update.state, update.selectionSet); sbPanel.updateTiming(); sbPanel.monitorHTMLOutput(); - if ( update.geometryChanged) { + if (update.geometryChanged) { sbPanel.updateSizing(update.view); } if (update.docChanged) { diff --git a/src/web/waiters/ControlsWaiter.mjs b/src/web/waiters/ControlsWaiter.mjs index 3ef359dd18..497219543b 100755 --- a/src/web/waiters/ControlsWaiter.mjs +++ b/src/web/waiters/ControlsWaiter.mjs @@ -429,8 +429,8 @@ ${navigator.userAgent} */ onMaximiseButtonClick(e) { // the target pane is not already maximised because it does not have the 'maximised-pane' class.. - const maximise = !document.getElementById(e.currentTarget.id.replace('maximise-', '')).classList.contains("maximised-pane"); - this.setPaneMaximised(e.currentTarget.id.replace('maximise-', ''), maximise); + const maximise = !document.getElementById(e.currentTarget.id.replace("maximise-", "")).classList.contains("maximised-pane"); + this.setPaneMaximised(e.currentTarget.id.replace("maximise-", ""), maximise); } /** @@ -440,17 +440,17 @@ ${navigator.userAgent} * @param {string} paneId * @param {boolean} maximise */ - setPaneMaximised( paneId, maximise ){ - const pane = document.getElementById( paneId ); - const btn = document.getElementById( `maximise-${paneId}` ); + setPaneMaximised(paneId, maximise) { + const pane = document.getElementById(paneId); + const btn = document.getElementById(`maximise-${paneId}`); this.setMaximiseControlButton(btn, maximise); this.setPaneMaximisedClasses(pane, maximise); - if ( maximise ) { + if (maximise) { pane.style.height = `${window.innerHeight - 40}px`; } else { - if ( window.innerWidth < this.app.breakpoint ){ + if (window.innerWidth < this.app.breakpoint) { this.app.assignAvailableHeight(); } } @@ -463,7 +463,7 @@ ${navigator.userAgent} * @param {boolean} maximise */ setPaneMaximisedClasses(pane, maximise) { - if ( maximise ) { + if (maximise) { pane.classList.add("top-zindex"); pane.classList.add("maximised-pane"); } else { @@ -480,8 +480,8 @@ ${navigator.userAgent} * @param {HTMLElement} btn * @param {boolean} maximise */ - setMaximiseControlButton(btn, maximise ) { - if ( maximise ) { + setMaximiseControlButton(btn, maximise) { + if (maximise) { btn.querySelector("i").innerHTML = "fullscreen_exit"; btn.setAttribute("data-original-title", "Reset pane"); } else { diff --git a/src/web/waiters/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs index 8a3fda05d0..15a7502997 100755 --- a/src/web/waiters/OperationsWaiter.mjs +++ b/src/web/waiters/OperationsWaiter.mjs @@ -39,13 +39,13 @@ class OperationsWaiter { searchOperations(e) { let ops, selected; - if (e.type === "keyup"){ + if (e.type === "keyup") { const searchResults = document.getElementById("search-results"); this.openOperationsDropdown(); - if ( e.target.value.length !== 0 ){ - this.app.updateVisibility(searchResults, true ); + if (e.target.value.length !== 0) { + this.app.updateVisibility(searchResults, true); } } @@ -60,9 +60,9 @@ class OperationsWaiter { } } - if (e.type === "click" && !e.target.value.length){ + if (e.type === "click" && !e.target.value.length) { this.openOperationsDropdown(); - } else if (e.keyCode === 27 ) { // Escape + } else if (e.keyCode === 27) { // Escape this.closeOperationsDropdown(); } else if (e.keyCode === 40) { // Down e.preventDefault(); @@ -184,7 +184,7 @@ class OperationsWaiter { * @param {event} e */ opListCreate(e) { - if ( window.innerWidth < this.app.breakpoint ){ + if (window.innerWidth < this.app.breakpoint) { this.mobileOpListCreate(e); } else { this.desktopOpListCreate(e); @@ -197,7 +197,7 @@ class OperationsWaiter { * * @param {event} e */ - desktopOpListCreate(e){ + desktopOpListCreate(e) { this.manager.recipe.createSortableSeedList(e.target); this.enableOpPopover(e.target); } @@ -208,7 +208,7 @@ class OperationsWaiter { * * @param {event} e */ - mobileOpListCreate(e){ + mobileOpListCreate(e) { this.manager.recipe.createSortableSeedList(e.target, false); this.disableOpsListPopovers(); } @@ -324,7 +324,7 @@ class OperationsWaiter { /** * Open operations dropdown */ - openOperationsDropdown(){ + openOperationsDropdown() { // the 'close' ( dropdown ) icon in Operations component mobile UI const closeOperationsDropdown = document.getElementById("close-operations-dropdown"); const categories = document.getElementById("categories"); @@ -338,12 +338,12 @@ class OperationsWaiter { * Hide any operation lists ( #categories or #search-results ) and the close-operations-dropdown * icon itself, clear any search input */ - closeOperationsDropdown(){ + closeOperationsDropdown() { const search = document.getElementById("search"); // if any input remains in #search, clear it if (search.value.length) { - search.value = ''; + search.value = ""; } this.app.updateVisibility(document.getElementById("categories"), false); @@ -380,7 +380,7 @@ class OperationsWaiter { * * @param {Event} e */ - onIconFavouriteClick(e){ + onIconFavouriteClick(e) { this.app.addFavourite(e.target.getAttribute("title")); } } diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index f94418f074..0f2886a02a 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -595,14 +595,14 @@ class RecipeWaiter { * This hides all the checkmark icons of previously added ( selected ) * operations to the recipe list */ - clearAllSelectedClasses(){ + clearAllSelectedClasses() { const list = document.querySelectorAll(".operation.selected"); // check if any operations are selected at all to prevent errors - if (list.length){ + if (list.length) { list.forEach((item) => { item.classList.remove("selected"); - }) + }); } } @@ -613,15 +613,15 @@ class RecipeWaiter { * * @param {string} opDataName the data-name of the target operation */ - addSelectedClass(opDataName){ + addSelectedClass(opDataName) { const list = document.querySelectorAll(".operation"); - const item = Array.from(list).filter((item) => item.getAttribute("data-name") === opDataName ); + const item = Array.from(list).filter((item) => item.getAttribute("data-name") === opDataName); // when an item is listed in favourites, there are 2 of // them and both need the 'selected' class ( checkmark ) item.forEach((op) => { op.classList.add("selected"); - }) + }); } /** @@ -636,24 +636,22 @@ class RecipeWaiter { * code ), I'd recommend to refactor this at one point, but that would mean a huge code * overhaul for another time / issue. */ - updateSelectedOperations(){ - let recipeList, operations; - - recipeList = document.querySelectorAll("#rec-list > li"); - operations = document.querySelectorAll(".operation"); + updateSelectedOperations() { + const recipeList = document.querySelectorAll("#rec-list > li"); + const operations = document.querySelectorAll(".operation"); this.clearAllSelectedClasses(); - if ( recipeList.length ){ + if (recipeList.length) { recipeList.forEach((ingredient) => { const ingredientName = ingredient.getAttribute("data-name"); operations.forEach((operation) => { - if ( ingredientName === operation.getAttribute("data-name")){ + if (ingredientName === operation.getAttribute("data-name")) { this.addSelectedClass(ingredientName); } - }) - }) + }); + }); } } } diff --git a/src/web/waiters/WindowWaiter.mjs b/src/web/waiters/WindowWaiter.mjs index da3a4899da..b688e92b79 100755 --- a/src/web/waiters/WindowWaiter.mjs +++ b/src/web/waiters/WindowWaiter.mjs @@ -30,7 +30,7 @@ class WindowWaiter { * continuous resetting). */ windowResize() { - if ( window.innerWidth >= this.app.breakpoint ) { + if (window.innerWidth >= this.app.breakpoint) { this.onResizeToDesktop(); } else { this.onResizeToMobile(); @@ -38,35 +38,41 @@ class WindowWaiter { // #output can be maximised on all screen sizes, so if it was open while resizing, // it can be kept maximised until minimised manually - if ( document.getElementById("output").classList.contains("maximised-pane") ) { - this.manager.controls.setPaneMaximised( "output", true ); + if (document.getElementById("output").classList.contains("maximised-pane")) { + this.manager.controls.setPaneMaximised("output", true); } debounce(this.app.adjustComponentSizes, 200, "windowResize", this.app, [])(); } - onResizeToDesktop(){ + /** + * Set desktop UI and close #recipe / #input maximised panes if there were any. + * Correct the height of #recipe + */ + onResizeToDesktop() { this.app.setDesktopUI(false); // if a window is resized past breakpoint while #recipe or #input is maximised, // the maximised pane is set to its default ( non-maximised ) state - ["recipe", "input"].forEach( paneId => this.manager.controls.setPaneMaximised(paneId, false)); + ["recipe", "input"].forEach(paneId => this.manager.controls.setPaneMaximised(paneId, false)); // to prevent #recipe from keeping the height set in divideAvailableSpace document.getElementById("recipe").style.height = "100%"; } - - onResizeToMobile(){ + /** + * Set mobile UI and close any maximised panes if there were any + */ + onResizeToMobile() { this.app.setMobileUI(); // when mobile devices' keyboards pop up, it triggers a window resize event. Here // we keep the maximised panes open until the minimise button is clicked / tapped ["recipe", "input", "output"] - .map( paneId => document.getElementById(paneId)) - .filter( pane => pane.classList.contains("maximised-pane")) - .forEach( pane => this.manager.controls.setPaneMaximised(pane.id, true)); + .map(paneId => document.getElementById(paneId)) + .filter(pane => pane.classList.contains("maximised-pane")) + .forEach(pane => this.manager.controls.setPaneMaximised(pane.id, true)); }