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
This commit is contained in:
parent
9d3567e0e6
commit
d020c53dfb
@ -67,6 +67,7 @@ class App {
|
|||||||
|
|
||||||
this.loadLocalStorage();
|
this.loadLocalStorage();
|
||||||
this.buildCategoryList();
|
this.buildCategoryList();
|
||||||
|
this.setCompileMessage();
|
||||||
this.buildUI();
|
this.buildUI();
|
||||||
this.manager.setup();
|
this.manager.setup();
|
||||||
this.manager.output.saveBombe();
|
this.manager.output.saveBombe();
|
||||||
@ -385,14 +386,20 @@ class App {
|
|||||||
this.updateFavourites(this.dfavourites, true);
|
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.
|
* Adds an operation to the user's favourites.
|
||||||
*
|
*
|
||||||
* @param {string} name - The name of the operation
|
* @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);
|
const favourites = JSON.parse(localStorage.favourites);
|
||||||
|
|
||||||
if (favourites.indexOf(name) >= 0) {
|
if (favourites.indexOf(name) >= 0) {
|
||||||
@ -401,7 +408,25 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
favourites.push(name);
|
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");
|
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 )
|
* Set desktop UI ( on init and on window resize events )
|
||||||
*/
|
*/
|
||||||
setDesktopUI() {
|
setDesktopUI() {
|
||||||
this.setCompileMessage();
|
this.setDownloadLinkVisibility(true);
|
||||||
this.setSplitter();
|
this.setSplitter();
|
||||||
|
|
||||||
this.manager.input.calcMaxTabs();
|
this.manager.input.calcMaxTabs();
|
||||||
@ -825,7 +859,9 @@ class App {
|
|||||||
* Set mobile UI ( on init and on window resize events )
|
* Set mobile UI ( on init and on window resize events )
|
||||||
*/
|
*/
|
||||||
setMobileUI() {
|
setMobileUI() {
|
||||||
|
this.setDownloadLinkVisibility(false);
|
||||||
this.setSplitter(false);
|
this.setSplitter(false);
|
||||||
|
|
||||||
this.assignAvailableHeight();
|
this.assignAvailableHeight();
|
||||||
$("[data-toggle=tooltip]").tooltip("disable");
|
$("[data-toggle=tooltip]").tooltip("disable");
|
||||||
}
|
}
|
||||||
@ -859,6 +895,9 @@ class App {
|
|||||||
* Build a CCategoryList component and append it to #categories
|
* Build a CCategoryList component and append it to #categories
|
||||||
*/
|
*/
|
||||||
buildCategoryList() {
|
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,
|
// double-check if the c-category-list already exists,
|
||||||
if (document.querySelector("#categories > c-category-list")) {
|
if (document.querySelector("#categories > c-category-list")) {
|
||||||
// then destroy it
|
// then destroy it
|
||||||
|
|||||||
@ -105,6 +105,10 @@ export class CCategoryLi extends HTMLElement {
|
|||||||
|
|
||||||
a.innerText = this.label;
|
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") {
|
if (this.label === "Favourites") {
|
||||||
const editFavouritesButton = this.buildEditFavouritesButton();
|
const editFavouritesButton = this.buildEditFavouritesButton();
|
||||||
|
|
||||||
@ -119,9 +123,20 @@ export class CCategoryLi extends HTMLElement {
|
|||||||
<li><b>To remove:</b> Click on the 'Edit favourites' button and hit the delete button next to the operation you want to remove</li>
|
<li><b>To remove:</b> Click on the 'Edit favourites' button and hit the delete button next to the operation you want to remove</li>
|
||||||
</ul>`);
|
</ul>`);
|
||||||
|
|
||||||
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;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import url from "url";
|
import url from "url";
|
||||||
|
import Utils from "../../core/Utils.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* c(ustom element)-operation-li ( list item )
|
* 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
|
// Use mousedown event instead of click to prevent accidentally firing the handler twice on mobile
|
||||||
this.addEventListener("mousedown", this.handleMousedown.bind(this));
|
this.addEventListener("mousedown", this.handleMousedown.bind(this));
|
||||||
this.addEventListener("dblclick", this.handleDoubleClick.bind(this));
|
this.addEventListener("dblclick", this.handleDoubleClick.bind(this));
|
||||||
|
this.addEventListener("touchstart", this.handleTouchStart.bind(this));
|
||||||
|
|
||||||
if (this.includeStarIcon) {
|
if (this.includeStarIcon) {
|
||||||
this.observer = new MutationObserver(this.updateFavourite.bind(this));
|
this.observer = new MutationObserver(this.updateFavourite.bind(this));
|
||||||
@ -50,6 +52,7 @@ export class COperationLi extends HTMLElement {
|
|||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
this.removeEventListener("mousedown", this.handleMousedown.bind(this));
|
this.removeEventListener("mousedown", this.handleMousedown.bind(this));
|
||||||
this.removeEventListener("dblclick", this.handleDoubleClick.bind(this));
|
this.removeEventListener("dblclick", this.handleDoubleClick.bind(this));
|
||||||
|
this.removeEventListener("touchstart", this.handleTouchStart.bind(this));
|
||||||
|
|
||||||
if (this.includeStarIcon) {
|
if (this.includeStarIcon) {
|
||||||
this.observer.disconnect();
|
this.observer.disconnect();
|
||||||
@ -62,6 +65,7 @@ export class COperationLi extends HTMLElement {
|
|||||||
* Handle double click
|
* Handle double click
|
||||||
*/
|
*/
|
||||||
handleDoubleClick() {
|
handleDoubleClick() {
|
||||||
|
this.app.manager.ops.clearSingleTapAlerts();
|
||||||
this.app.manager.recipe.addOperation(this.operationName);
|
this.app.manager.recipe.addOperation(this.operationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,13 +76,30 @@ export class COperationLi extends HTMLElement {
|
|||||||
*/
|
*/
|
||||||
handleMousedown(e) {
|
handleMousedown(e) {
|
||||||
if (e.target === this.querySelector("i.star-icon")) {
|
if (e.target === this.querySelector("i.star-icon")) {
|
||||||
this.app.addFavourite(this.operationName);
|
if (!this.isFavourite) {
|
||||||
}
|
this.app.addFavourite(this.operationName);
|
||||||
// current use case: in the 'Edit favourites' modal, the c-operation-li components have a trashcan icon to the
|
this.isFavourite = true;
|
||||||
// right
|
} else {
|
||||||
if (e.target === this.querySelector("i.remove-icon")) {
|
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();
|
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 = "";
|
pageTitle = "";
|
||||||
|
|
||||||
switch (urlObj.host) {
|
switch (urlObj.host) {
|
||||||
case "forensicswiki.xyz":
|
case "forensics.wiki":
|
||||||
wikiName = "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;
|
break;
|
||||||
case "wikipedia.org":
|
case "wikipedia.org":
|
||||||
wikiName = "Wikipedia";
|
wikiName = "Wikipedia";
|
||||||
|
|||||||
@ -148,13 +148,13 @@
|
|||||||
</button>
|
</button>
|
||||||
<div id="content-wrapper">
|
<div id="content-wrapper">
|
||||||
<div id="banner">
|
<div id="banner">
|
||||||
<div>
|
<div id="download-wrapper">
|
||||||
<a href="#" class="banner-link" data-toggle="modal" data-target="#download-modal" data-help-title="Downloading CyberChef" data-help="<p>CyberChef can be downloaded to run locally or hosted within your own network. It has no server-side component so all that is required is that the ZIP file is uncompressed and the files are accessible.</p><p>As a user, it is worth noting that unofficial versions of CyberChef could have been modified to introduce Input and/or Recipe exfiltration. We recommend always using the official, open source, up-to-date version of CyberChef hosted at <a href='https://gchq.github.io/CyberChef'>https://gchq.github.io/CyberChef</a> if accessible.</p><p>The Network tab in your browser's Developer console (F12) can be used to inspect the network requests made by a website. This can confirm that no data is uploaded when a CyberChef recipe is baked.</p>">
|
<a href="#" class="banner-link desktop-only" data-toggle="modal" data-target="#download-modal" data-help-title="Downloading CyberChef" data-help="<p>CyberChef can be downloaded to run locally or hosted within your own network. It has no server-side component so all that is required is that the ZIP file is uncompressed and the files are accessible.</p><p>As a user, it is worth noting that unofficial versions of CyberChef could have been modified to introduce Input and/or Recipe exfiltration. We recommend always using the official, open source, up-to-date version of CyberChef hosted at <a href='https://gchq.github.io/CyberChef'>https://gchq.github.io/CyberChef</a> if accessible.</p><p>The Network tab in your browser's Developer console (F12) can be used to inspect the network requests made by a website. This can confirm that no data is uploaded when a CyberChef recipe is baked.</p>">
|
||||||
<span>Download CyberChef</span>
|
<span>Download CyberChef</span>
|
||||||
<i class="material-icons">file_download</i>
|
<i class="material-icons banner-icon">file_download</i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="notice-wrapper" class="desktop-only">
|
<div id="notice-wrapper">
|
||||||
<span id="notice">
|
<span id="notice">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
|
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
|
||||||
@ -173,7 +173,7 @@
|
|||||||
data-help-title="Options and Settings"
|
data-help-title="Options and Settings"
|
||||||
data-help="Configurable options to change how CyberChef behaves. These settings are stored in your browser's local storage, meaning they will persist between sessions that use the same browser profile.">
|
data-help="Configurable options to change how CyberChef behaves. These settings are stored in your browser's local storage, meaning they will persist between sessions that use the same browser profile.">
|
||||||
<span class="desktop-only">Options</span>
|
<span class="desktop-only">Options</span>
|
||||||
<i class="material-icons">settings</i>
|
<i class="material-icons banner-icon">settings</i>
|
||||||
</a>
|
</a>
|
||||||
<a href="#"
|
<a href="#"
|
||||||
id="support"
|
id="support"
|
||||||
@ -183,7 +183,7 @@
|
|||||||
data-help-title="About / Support"
|
data-help-title="About / Support"
|
||||||
data-help="This pane provides information about the CyberChef web app, how to use some of the features, and how to raise bug reports.">
|
data-help="This pane provides information about the CyberChef web app, how to use some of the features, and how to raise bug reports.">
|
||||||
<span class="desktop-only">About / Support</span>
|
<span class="desktop-only">About / Support</span>
|
||||||
<i class="material-icons">help</i>
|
<i class="material-icons banner-icon">help</i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -193,15 +193,17 @@
|
|||||||
data-help-title="Operations list"
|
data-help-title="Operations list"
|
||||||
data-help="<p>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.</p><p>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).</p>">
|
data-help="<p>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.</p><p>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).</p>">
|
||||||
Operations
|
Operations
|
||||||
<span class="op-count"></span>
|
<div id="ops-title-right">
|
||||||
<span class="pane-controls">
|
<span class="op-count"></span>
|
||||||
<button type="button"
|
<span class="pane-controls">
|
||||||
class="btn bmd-btn-icon mobile-only hidden"
|
<button type="button"
|
||||||
id="close-ops-dropdown-icon"
|
class="btn bmd-btn-icon mobile-only hidden"
|
||||||
title="Close dropdown">
|
id="close-ops-dropdown-icon"
|
||||||
<i class="material-icons">close</i>
|
title="Close dropdown">
|
||||||
</button>
|
<i class="material-icons">close</i>
|
||||||
</span>
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="operations-wrapper">
|
<div id="operations-wrapper">
|
||||||
<input id="search"
|
<input id="search"
|
||||||
|
|||||||
@ -47,5 +47,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.banner-icon {
|
||||||
|
transform: translateY(-1.5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,8 @@
|
|||||||
font-size: initial;
|
font-size: initial;
|
||||||
color: var(--primary-font-colour);
|
color: var(--primary-font-colour);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#auto-bake-label .check,
|
#auto-bake-label .check,
|
||||||
@ -65,6 +67,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#auto-bake-label .checkbox-decorator {
|
#auto-bake-label .checkbox-decorator {
|
||||||
|
display: inline-flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@ -111,7 +111,6 @@
|
|||||||
|
|
||||||
/* Right hand side icons */
|
/* Right hand side icons */
|
||||||
.rhs {
|
.rhs {
|
||||||
position: fixed;
|
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,3 +59,14 @@ c-category-list > ul {
|
|||||||
padding: 8px 8px 9px 8px;
|
padding: 8px 8px 9px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-title-right {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.op-count {
|
||||||
|
color: var(--subtext-font-colour);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: xx-small;
|
||||||
|
opacity: 0.5;
|
||||||
|
padding-left: .5em;
|
||||||
|
}
|
||||||
@ -13,6 +13,11 @@
|
|||||||
padding: 8px 8px 8px 12px;
|
padding: 8px 8px 8px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#operations-title-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
#operations-wrapper {
|
#operations-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,9 @@ class OperationsWaiter {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
|
||||||
this.options = {};
|
this.options = {};
|
||||||
|
|
||||||
|
this.lastSingleTapAlert = null;
|
||||||
|
this.singleTapAlertTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -174,64 +177,6 @@ class OperationsWaiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for edit favourites click events.
|
|
||||||
* Displays the 'Edit favourites' modal and handles the c-operation-list in the modal
|
|
||||||
*
|
|
||||||
* @param {Event} e
|
|
||||||
*/
|
|
||||||
opListCreate(e) {
|
|
||||||
this.manager.recipe.createSortableSeedList(e.target);
|
|
||||||
|
|
||||||
// Populate ops total
|
|
||||||
document.querySelector("#operations .title .op-count").innerText = Object.keys(this.app.operations).length;
|
|
||||||
|
|
||||||
this.enableOpsListPopovers(e.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up popovers, allowing the popover itself to gain focus which enables scrolling
|
|
||||||
* and other interactions.
|
|
||||||
*
|
|
||||||
* @param {Element} el - The element to start selecting from
|
|
||||||
*/
|
|
||||||
enableOpsListPopovers(el) {
|
|
||||||
$(el).find("[data-toggle=popover]").addBack("[data-toggle=popover]")
|
|
||||||
.popover({trigger: "manual"})
|
|
||||||
.on("mouseenter", function(e) {
|
|
||||||
if (e.buttons > 0) return; // Mouse button held down - likely dragging an operation
|
|
||||||
const _this = this;
|
|
||||||
$(this).popover("show");
|
|
||||||
$(".popover").on("mouseleave", function () {
|
|
||||||
$(_this).popover("hide");
|
|
||||||
});
|
|
||||||
}).on("mouseleave", function () {
|
|
||||||
const _this = this;
|
|
||||||
setTimeout(function() {
|
|
||||||
// Determine if the popover associated with this element is being hovered over
|
|
||||||
if ($(_this).data("bs.popover") &&
|
|
||||||
($(_this).data("bs.popover").tip && !$($(_this).data("bs.popover").tip).is(":hover"))) {
|
|
||||||
$(_this).popover("hide");
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for operation doubleclick events.
|
|
||||||
* Adds the operation to the recipe and auto bakes.
|
|
||||||
*
|
|
||||||
* @param {event} e
|
|
||||||
*/
|
|
||||||
operationDblclick(e) {
|
|
||||||
const li = e.target;
|
|
||||||
|
|
||||||
this.manager.recipe.addOperation(li.textContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for edit favourites click events.
|
* Handler for edit favourites click events.
|
||||||
* Sets up the 'Edit favourites' pane and displays it.
|
* Sets up the 'Edit favourites' pane and displays it.
|
||||||
@ -323,18 +268,41 @@ class OperationsWaiter {
|
|||||||
this.app.resetFavourites();
|
this.app.resetFavourites();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether operation counts are displayed next to a category title
|
* Sets whether operation counts are displayed next to a category title
|
||||||
*/
|
*/
|
||||||
setCatCount() {
|
setCatCount() {
|
||||||
if (this.app.options.showCatCount) {
|
if (this.app.options.showCatCount) {
|
||||||
document.querySelectorAll(".category-title .op-count").forEach(el => el.classList.remove("hidden"));
|
document.querySelectorAll(".category-title .category-title-right .op-count").forEach(el => el.classList.remove("hidden"));
|
||||||
} else {
|
} else {
|
||||||
document.querySelectorAll(".category-title .op-count").forEach(el => el.classList.add("hidden"));
|
document.querySelectorAll(".category-title .category-title-right .op-count").forEach(el => el.classList.add("hidden"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles alerts for single tap events on mobile. Will disallow more than one every
|
||||||
|
* 30 seconds.
|
||||||
|
*/
|
||||||
|
sendSingleTapAlert() {
|
||||||
|
clearTimeout(this.singleTapAlertTimeout);
|
||||||
|
this.singleTapAlertTimeout = setTimeout(() => {
|
||||||
|
const now = new Date();
|
||||||
|
// Only display alert if we've not seen one before or in at least 30 seconds.
|
||||||
|
if (!this.lastSingleTapAlert || now.getTime() - this.lastSingleTapAlert.getTime() > 30_000) {
|
||||||
|
this.lastSingleTapAlert = now;
|
||||||
|
this.app.alert("Double tap an operation to add it to your recipe.", 3000);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear pending single tap alerts.
|
||||||
|
*/
|
||||||
|
clearSingleTapAlerts() {
|
||||||
|
clearTimeout(this.singleTapAlertTimeout);
|
||||||
|
this.singleTapAlertTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OperationsWaiter;
|
export default OperationsWaiter;
|
||||||
|
|||||||
@ -63,6 +63,7 @@ class RecipeWaiter {
|
|||||||
onEnd: function(e) {
|
onEnd: function(e) {
|
||||||
if (this.removeIntent) {
|
if (this.removeIntent) {
|
||||||
e.item.remove();
|
e.item.remove();
|
||||||
|
document.dispatchEvent(this.manager.statechange);
|
||||||
e.target.dispatchEvent(this.manager.operationremove);
|
e.target.dispatchEvent(this.manager.operationremove);
|
||||||
}
|
}
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user