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 @@