mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-07 15:07:11 -04:00
203 lines
6.9 KiB
JavaScript
203 lines
6.9 KiB
JavaScript
import {COperationLi} from "./c-operation-li.mjs";
|
|
import Sortable from "sortablejs";
|
|
|
|
/**
|
|
* c(ustom element)-operation-list
|
|
*/
|
|
export class COperationList extends HTMLElement {
|
|
/**
|
|
* @param {App} app - The main view object for CyberChef
|
|
* @param {[string, number[]]} operations - A list of operation names and indexes of characters to highlight
|
|
* @param {Boolean} includeStarIcon - Include the left side 'star' icon to each of the c-category-li >
|
|
* c-operation-list > c-operation-li list items in this c-category-list
|
|
* @param {Boolean} isSortable - List items may be sorted ( reordered ). False by default
|
|
* @param {Boolean} isCloneable - List items are cloneable to a target list. True by default
|
|
* @param {Object} icon ( { class: string, innerText: string } ). 'check-icon' by default
|
|
*/
|
|
constructor(
|
|
app,
|
|
operations,
|
|
includeStarIcon,
|
|
isSortable = false,
|
|
isCloneable = true,
|
|
icon
|
|
) {
|
|
super();
|
|
|
|
this.app = app;
|
|
this.operations = operations;
|
|
this.includeStarIcon = includeStarIcon;
|
|
this.isSortable = isSortable;
|
|
this.isCloneable = isCloneable;
|
|
this.icon = icon;
|
|
|
|
this.build();
|
|
|
|
window.addEventListener("operationadd", this.handleChange.bind(this));
|
|
window.addEventListener("operationremove", this.handleChange.bind(this));
|
|
window.addEventListener("favouritesupdate", this.handleChange.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Remove listeners on disconnectedCallback
|
|
*/
|
|
disconnectedCallback() {
|
|
this.removeEventListener("operationadd", this.handleChange.bind(this));
|
|
this.removeEventListener("operationremove", this.handleChange.bind(this));
|
|
this.removeEventListener("favouritesupdate", this.handleChange.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Handle change
|
|
* Fires on custom operationadd, operationremove, favouritesupdate events
|
|
*/
|
|
handleChange() {
|
|
this.updateListItemsClasses("#catFavourites c-operation-list ul", "favourite");
|
|
this.updateListItemsClasses("#rec-list", "selected");
|
|
}
|
|
|
|
/**
|
|
* Build c-operation-list
|
|
*
|
|
* @returns {HTMLElement}
|
|
*/
|
|
build() {
|
|
const ul = document.createElement("ul");
|
|
ul.classList.add("op-list");
|
|
|
|
this.operations.forEach((([opName, charIndicesToHighlight]) => {
|
|
const cOpLi = new COperationLi(
|
|
this.app,
|
|
opName,
|
|
{
|
|
class: this.icon ? this.icon.class : "check-icon",
|
|
innerText: this.icon ? this.icon.innerText : "check"
|
|
},
|
|
this.includeStarIcon,
|
|
charIndicesToHighlight
|
|
);
|
|
|
|
ul.appendChild(cOpLi);
|
|
}));
|
|
|
|
if (this.isSortable) {
|
|
this.createSortableList(ul);
|
|
} else if (!this.app.isMobileView() && this.isCloneable) {
|
|
this.createCloneableList(ul, "recipe", "rec-list");
|
|
}
|
|
|
|
this.append(ul);
|
|
}
|
|
|
|
/**
|
|
* Create a sortable ( but not cloneable ) list
|
|
*
|
|
* @param { HTMLElement } ul
|
|
* */
|
|
createSortableList(ul) {
|
|
const sortableList = Sortable.create(ul, {
|
|
group: "sorting",
|
|
sort: true,
|
|
draggable: "c-operation-li",
|
|
filter: "i.material-icons",
|
|
onFilter: function (e) {
|
|
const el = sortableList.closest(e.item);
|
|
if (el && el.parentNode) {
|
|
$(el).popover("dispose");
|
|
el.parentNode.removeChild(el);
|
|
}
|
|
},
|
|
onEnd: function(e) {
|
|
if (this.app.manager.recipe.removeIntent) {
|
|
$(e.item).popover("dispose");
|
|
e.item.remove();
|
|
}
|
|
}.bind(this),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a cloneable ( not sortable ) list
|
|
*
|
|
* @param { HTMLElement } ul
|
|
* @param { string } targetListName
|
|
* @param { string } targetListId
|
|
* */
|
|
createCloneableList(ul, targetListName, targetListId) {
|
|
let dragOverRecList = false;
|
|
const recList = document.querySelector(`#${targetListId}`);
|
|
|
|
Sortable.utils.on(recList, "dragover", function () {
|
|
dragOverRecList = true;
|
|
});
|
|
|
|
Sortable.utils.on(recList, "dragleave", function () {
|
|
dragOverRecList = false;
|
|
});
|
|
|
|
Sortable.create(ul, {
|
|
group: {
|
|
name: targetListName,
|
|
pull: "clone",
|
|
put: false,
|
|
},
|
|
draggable: "c-operation-li",
|
|
sort: false,
|
|
setData: function(dataTransfer, dragEl) {
|
|
dataTransfer.setData("Text", dragEl.querySelector("li").getAttribute("data-name"));
|
|
},
|
|
onStart: function(e) {
|
|
// Removes popover element and event bindings from the dragged operation but not the
|
|
// event bindings from the one left in the operations list. Without manually removing
|
|
// these bindings, we cannot re-initialise the popover on the stub operation.
|
|
$(e.item)
|
|
.find("[data-toggle=popover]")
|
|
.popover("dispose");
|
|
$(e.clone)
|
|
.find("[data-toggle=popover]")
|
|
.off(".popover")
|
|
.removeData("bs.popover");
|
|
},
|
|
onEnd: ({item, to}) => {
|
|
if (item.parentNode.id === targetListId && dragOverRecList) {
|
|
this.app.manager.recipe.addOperation(item.name);
|
|
item.remove();
|
|
} else if (!dragOverRecList && !to.classList.contains("op-list")) {
|
|
item.remove();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update classes ( className ) on the li.operation elements in this list, based on the current state of a
|
|
* list of choice ( srcListSelector )
|
|
*
|
|
* @param {string} srcListSelector - the selector of the UL that we want to use as source of truth
|
|
* @param {string} className - the className to update
|
|
*/
|
|
updateListItemsClasses(srcListSelector, className) {
|
|
const srcListItems= document.querySelectorAll(`${srcListSelector} li`);
|
|
const listItems = this.querySelectorAll("c-operation-li li.operation");
|
|
|
|
listItems.forEach((li => {
|
|
if (li.classList.contains(`${className}`)) {
|
|
li.classList.remove(`${className}`);
|
|
}
|
|
}));
|
|
|
|
if (srcListItems.length !== 0) {
|
|
srcListItems.forEach((item => {
|
|
const targetDataName = item.getAttribute("data-name");
|
|
|
|
listItems.forEach((listItem) => {
|
|
if (targetDataName === listItem.getAttribute("data-name")) {
|
|
listItem.classList.add(`${className}`);
|
|
}
|
|
});
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
customElements.define("c-operation-list", COperationList);
|