From d020c53dfb6ecfdd87dcbe39954de30c61da96af Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 6 Feb 2026 15:37:20 -0500 Subject: [PATCH] Update to support added features since last master merge - Operation counts - Allow toggling on favourite icon - Notify users that a single tap does nothing in the operations list - Fix odd checkbox label position on macos - Fix odd label pinning - History update now happens on drag operation removal - Show compile message rather than "download cyberchef" in banner --- src/web/App.mjs | 47 +++++++++- src/web/components/c-category-li.mjs | 17 +++- src/web/components/c-operation-li.mjs | 35 ++++++-- src/web/html/index.html | 32 +++---- src/web/stylesheets/components/_banner.css | 4 + src/web/stylesheets/components/_controls.css | 3 + .../stylesheets/components/io/_status-bar.css | 1 - .../components/operations/_categories.css | 11 +++ .../components/operations/_operations.css | 5 ++ src/web/waiters/OperationsWaiter.mjs | 90 ++++++------------- src/web/waiters/RecipeWaiter.mjs | 1 + 11 files changed, 157 insertions(+), 89 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index 3091bf1a6..e18d9116d 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -67,6 +67,7 @@ class App { this.loadLocalStorage(); this.buildCategoryList(); + this.setCompileMessage(); this.buildUI(); this.manager.setup(); this.manager.output.saveBombe(); @@ -385,14 +386,20 @@ class App { this.updateFavourites(this.dfavourites, true); } + /** + * Checks if the favourites category is expanded + */ + isFavouritesExpanded() { + const catFavourites = document.getElementById("#catFavourites"); + return catFavourites ? catFavourites.classList.contains("show") : false; + } /** * Adds an operation to the user's favourites. * * @param {string} name - The name of the operation - * @param {Boolean} isExpanded - false by default */ - addFavourite(name, isExpanded = false) { + addFavourite(name) { const favourites = JSON.parse(localStorage.favourites); if (favourites.indexOf(name) >= 0) { @@ -401,7 +408,25 @@ class App { } favourites.push(name); - this.updateFavourites(favourites, isExpanded); + this.updateFavourites(favourites, this.isFavouritesExpanded()); + } + + /** + * Removes an operation from the user's favourites. + * + * @param {string} name - The name of the operation + */ + removeFavourite(name) { + const favourites = JSON.parse(localStorage.favourites); + + const index = favourites.indexOf(name); + if (index === -1) { + this.alert(`'${name}' isn't in your favourites`, 3000); + return; + } + + favourites.splice(index, 1); + this.updateFavourites(favourites, this.isFavouritesExpanded()); } @@ -810,11 +835,20 @@ class App { return isVisible ? elm.classList.remove("hidden") : elm.classList.add("hidden"); } + /** + * Set visibility of download link + * + * @param {boolean} isVisible + */ + setDownloadLinkVisibility(isVisible) { + this.setElementVisibility(document.getElementById("download-wrapper"), true); + } + /** * Set desktop UI ( on init and on window resize events ) */ setDesktopUI() { - this.setCompileMessage(); + this.setDownloadLinkVisibility(true); this.setSplitter(); this.manager.input.calcMaxTabs(); @@ -825,7 +859,9 @@ class App { * Set mobile UI ( on init and on window resize events ) */ setMobileUI() { + this.setDownloadLinkVisibility(false); this.setSplitter(false); + this.assignAvailableHeight(); $("[data-toggle=tooltip]").tooltip("disable"); } @@ -859,6 +895,9 @@ class App { * Build a CCategoryList component and append it to #categories */ buildCategoryList() { + // populate total operations count + document.querySelector("#operations .title .op-count").innerText = Object.keys(this.operations).length; + // double-check if the c-category-list already exists, if (document.querySelector("#categories > c-category-list")) { // then destroy it diff --git a/src/web/components/c-category-li.mjs b/src/web/components/c-category-li.mjs index de8a1fbc9..c6ddb6c6f 100644 --- a/src/web/components/c-category-li.mjs +++ b/src/web/components/c-category-li.mjs @@ -105,6 +105,10 @@ export class CCategoryLi extends HTMLElement { a.innerText = this.label; + // Create wrapper for right-aligned controls + const divRight = document.createElement("div"); + divRight.setAttribute("class", "category-title-right"); + if (this.label === "Favourites") { const editFavouritesButton = this.buildEditFavouritesButton(); @@ -119,9 +123,20 @@ export class CCategoryLi extends HTMLElement {
  • To remove: Click on the 'Edit favourites' button and hit the delete button next to the operation you want to remove
  • `); - a.appendChild(editFavouritesButton); + divRight.appendChild(editFavouritesButton); } + // Show count of operations in category + const opCountSpan = document.createElement("span"); + opCountSpan.classList.add("op-count"); + if (!this.app.options.showCatCount) { + opCountSpan.classList.add("hidden"); + } + opCountSpan.innerText = this.category.ops.length; + divRight.appendChild(opCountSpan); + + a.appendChild(divRight); + return a; } diff --git a/src/web/components/c-operation-li.mjs b/src/web/components/c-operation-li.mjs index 64e650520..f8da5a886 100644 --- a/src/web/components/c-operation-li.mjs +++ b/src/web/components/c-operation-li.mjs @@ -1,4 +1,5 @@ import url from "url"; +import Utils from "../../core/Utils.mjs"; /** * c(ustom element)-operation-li ( list item ) @@ -37,6 +38,7 @@ export class COperationLi extends HTMLElement { // Use mousedown event instead of click to prevent accidentally firing the handler twice on mobile this.addEventListener("mousedown", this.handleMousedown.bind(this)); this.addEventListener("dblclick", this.handleDoubleClick.bind(this)); + this.addEventListener("touchstart", this.handleTouchStart.bind(this)); if (this.includeStarIcon) { this.observer = new MutationObserver(this.updateFavourite.bind(this)); @@ -50,6 +52,7 @@ export class COperationLi extends HTMLElement { disconnectedCallback() { this.removeEventListener("mousedown", this.handleMousedown.bind(this)); this.removeEventListener("dblclick", this.handleDoubleClick.bind(this)); + this.removeEventListener("touchstart", this.handleTouchStart.bind(this)); if (this.includeStarIcon) { this.observer.disconnect(); @@ -62,6 +65,7 @@ export class COperationLi extends HTMLElement { * Handle double click */ handleDoubleClick() { + this.app.manager.ops.clearSingleTapAlerts(); this.app.manager.recipe.addOperation(this.operationName); } @@ -72,13 +76,30 @@ export class COperationLi extends HTMLElement { */ handleMousedown(e) { if (e.target === this.querySelector("i.star-icon")) { - this.app.addFavourite(this.operationName); - } - // current use case: in the 'Edit favourites' modal, the c-operation-li components have a trashcan icon to the - // right - if (e.target === this.querySelector("i.remove-icon")) { + if (!this.isFavourite) { + this.app.addFavourite(this.operationName); + this.isFavourite = true; + } else { + this.app.removeFavourite(this.operationName); + this.isFavourite = false; + } + } else if (e.target === this.querySelector("i.remove-icon")) { + // current use case: in the 'Edit favourites' modal, the c-operation-li components have a trashcan icon to the + // right this.remove(); + } else { + return; } + // if we've handled another event, don't use this to trigger doubleclick + e.preventDefault(); + } + + /** + * If the user taps a single operation, alert them that doubletapping adds operation to recipe. + * @param {TouchEvent} e + */ + handleTouchStart(e) { + this.app.manager.ops.sendSingleTapAlert(); } /** @@ -157,9 +178,9 @@ export class COperationLi extends HTMLElement { pageTitle = ""; switch (urlObj.host) { - case "forensicswiki.xyz": + case "forensics.wiki": wikiName = "Forensics Wiki"; - pageTitle = urlObj.query.substr(6).replace(/_/g, " "); // Chop off 'title=' + pageTitle = Utils.toTitleCase(urlObj.path.replace(/\//g, "").replace(/_/g, " ")); break; case "wikipedia.org": wikiName = "Wikipedia"; diff --git a/src/web/html/index.html b/src/web/html/index.html index d0bf6816a..086bd743c 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -148,13 +148,13 @@