mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-08 07:21:02 -04:00
search result highlighted strings, do not add op to recipe on escape
This commit is contained in:
parent
85fff21068
commit
f176a1106a
14 changed files with 108 additions and 125 deletions
|
@ -265,7 +265,7 @@ class App {
|
||||||
if (this.ioSplitter) this.ioSplitter.destroy();
|
if (this.ioSplitter) this.ioSplitter.destroy();
|
||||||
|
|
||||||
this.columnSplitter = Split(["#operations", "#recipe", "#IO"], {
|
this.columnSplitter = Split(["#operations", "#recipe", "#IO"], {
|
||||||
sizes: [20, 30, 50],
|
sizes: [20, 40, 40],
|
||||||
minSize: minimise ? [0, 0, 0] : [360, 330, 310],
|
minSize: minimise ? [0, 0, 0] : [360, 330, 310],
|
||||||
gutterSize: 4,
|
gutterSize: 4,
|
||||||
expandToMin: true,
|
expandToMin: true,
|
||||||
|
@ -510,7 +510,7 @@ class App {
|
||||||
// Search for nearest match and add it
|
// Search for nearest match and add it
|
||||||
const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false);
|
const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false);
|
||||||
if (matchedOps.length) {
|
if (matchedOps.length) {
|
||||||
this.manager.recipe.addOperation(matchedOps[0].name);
|
this.manager.recipe.addOperation(matchedOps[0][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate search with the string
|
// Populate search with the string
|
||||||
|
|
|
@ -144,7 +144,7 @@ class Manager {
|
||||||
document.getElementById("maximise-output").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls));
|
document.getElementById("maximise-output").addEventListener("click", this.controls.onMaximiseButtonClick.bind(this.controls));
|
||||||
|
|
||||||
// Operations
|
// Operations
|
||||||
this.addMultiEventListener("#search", "keyup paste search click", this.ops.searchOperations, this.ops);
|
this.addMultiEventListener("#search", "keyup paste click", this.ops.searchOperations, this.ops);
|
||||||
document.getElementById("close-ops-dropdown-icon").addEventListener("click", this.ops.closeOpsDropdown.bind(this.ops));
|
document.getElementById("close-ops-dropdown-icon").addEventListener("click", this.ops.closeOpsDropdown.bind(this.ops));
|
||||||
document.getElementById("save-favourites").addEventListener("click", this.ops.saveFavouritesClick.bind(this.ops));
|
document.getElementById("save-favourites").addEventListener("click", this.ops.saveFavouritesClick.bind(this.ops));
|
||||||
document.getElementById("reset-favourites").addEventListener("click", this.ops.resetFavouritesClick.bind(this.ops));
|
document.getElementById("reset-favourites").addEventListener("click", this.ops.resetFavouritesClick.bind(this.ops));
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
- ignore dropped item outside of rec-list
|
|
||||||
- can only drag an op to favourites 1 time
|
|
||||||
- stupid popovers on deleting favs for instance ( dont always close nicely )
|
|
||||||
- UI tests etc.
|
|
||||||
- esc on selected-op search results will add that op to recipe
|
|
||||||
- highlight strings
|
|
||||||
- initial search is kinda slow
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ export class CCategoryLi extends HTMLElement {
|
||||||
|
|
||||||
const opList = new COperationList(
|
const opList = new COperationList(
|
||||||
this.app,
|
this.app,
|
||||||
this.category.ops,
|
this.category.ops.map( op => [op]),
|
||||||
this.includeOpLiStarIcon,
|
this.includeOpLiStarIcon,
|
||||||
false,
|
false,
|
||||||
true
|
true
|
||||||
|
@ -81,7 +81,6 @@ export class CCategoryLi extends HTMLElement {
|
||||||
buildListItem() {
|
buildListItem() {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
|
|
||||||
li.classList.add("panel");
|
|
||||||
li.classList.add("category");
|
li.classList.add("category");
|
||||||
|
|
||||||
return li;
|
return li;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import url from "url";
|
import url from "url";
|
||||||
import HTMLIngredient from "../HTMLIngredient.mjs";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* c(ustom element)-operation-li ( list item )
|
* c(ustom element)-operation-li ( list item )
|
||||||
|
@ -8,6 +7,7 @@ import HTMLIngredient from "../HTMLIngredient.mjs";
|
||||||
* @param {string} name - The name of the operation
|
* @param {string} name - The name of the operation
|
||||||
* @param {Object} icon - { class: string, innerText: string } - The optional and customizable icon displayed on the right side of the operation
|
* @param {Object} icon - { class: string, innerText: string } - The optional and customizable icon displayed on the right side of the operation
|
||||||
* @param {Boolean} includeStarIcon - Include the left side 'star' icon to favourite an operation easily
|
* @param {Boolean} includeStarIcon - Include the left side 'star' icon to favourite an operation easily
|
||||||
|
* @param {[number[]]} charIndicesToHighlight - optional array of indices that indicate characters to highlight (bold) in operation name
|
||||||
*/
|
*/
|
||||||
export class COperationLi extends HTMLElement {
|
export class COperationLi extends HTMLElement {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -15,6 +15,7 @@ export class COperationLi extends HTMLElement {
|
||||||
name,
|
name,
|
||||||
icon,
|
icon,
|
||||||
includeStarIcon,
|
includeStarIcon,
|
||||||
|
charIndicesToHighlight = []
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ export class COperationLi extends HTMLElement {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.includeStarIcon = includeStarIcon;
|
this.includeStarIcon = includeStarIcon;
|
||||||
|
this.charIndicesToHighlight = charIndicesToHighlight;
|
||||||
|
|
||||||
this.config = this.app.operations[name];
|
this.config = this.app.operations[name];
|
||||||
|
|
||||||
|
@ -54,7 +56,8 @@ export class COperationLi extends HTMLElement {
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
*/
|
*/
|
||||||
handleDoubleClick(e) {
|
handleDoubleClick(e) {
|
||||||
if (e.target === this.querySelector("li")) {
|
// Span contains operation title (highlighted or not)
|
||||||
|
if (e.target === this.querySelector("li") || e.target === this.querySelector("span")) {
|
||||||
this.app.manager.recipe.addOperation(this.name);
|
this.app.manager.recipe.addOperation(this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,6 +172,8 @@ export class COperationLi extends HTMLElement {
|
||||||
buildListItem() {
|
buildListItem() {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
|
|
||||||
|
li.appendChild( this.buildOperationName() );
|
||||||
|
|
||||||
li.setAttribute("data-name", this.name);
|
li.setAttribute("data-name", this.name);
|
||||||
li.classList.add("operation");
|
li.classList.add("operation");
|
||||||
|
|
||||||
|
@ -176,7 +181,6 @@ export class COperationLi extends HTMLElement {
|
||||||
li.classList.add("favourite");
|
li.classList.add("favourite");
|
||||||
}
|
}
|
||||||
|
|
||||||
li.textContent = this.name;
|
|
||||||
|
|
||||||
if (this.config.description){
|
if (this.config.description){
|
||||||
let dataContent = this.config.description;
|
let dataContent = this.config.description;
|
||||||
|
@ -193,7 +197,6 @@ export class COperationLi extends HTMLElement {
|
||||||
li.setAttribute("data-boundary", "viewport");
|
li.setAttribute("data-boundary", "viewport");
|
||||||
li.setAttribute("data-content", dataContent);
|
li.setAttribute("data-content", dataContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return li;
|
return li;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,59 +251,62 @@ export class COperationLi extends HTMLElement {
|
||||||
* with constructor arguments for sortable and cloneable lists
|
* with constructor arguments for sortable and cloneable lists
|
||||||
*/
|
*/
|
||||||
cloneNode() {
|
cloneNode() {
|
||||||
const { app, name, icon, includeStarIcon } = this;
|
const { app, name, icon, includeStarIcon, charIndicesToHighlight } = this;
|
||||||
return new COperationLi( app, name, icon, includeStarIcon );
|
return new COperationLi( app, name, icon, includeStarIcon, charIndicesToHighlight );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlights searched strings in the name and description of the operation.
|
* Highlights searched strings in the name and description of the operation.
|
||||||
*
|
|
||||||
* @param {[[number]]} nameIdxs - Indexes of the search strings in the operation name [[start, length]]
|
|
||||||
* @param {[[number]]} descIdxs - Indexes of the search strings in the operation description [[start, length]]
|
|
||||||
*/
|
*/
|
||||||
highlightSearchStrings(nameIdxs, descIdxs) {
|
buildOperationName() {
|
||||||
if (nameIdxs.length && typeof nameIdxs[0][0] === "number") {
|
const span = document.createElement('span');
|
||||||
|
|
||||||
|
if (this.charIndicesToHighlight.length) {
|
||||||
let opName = "",
|
let opName = "",
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
|
||||||
nameIdxs.forEach(idxs => {
|
this.charIndicesToHighlight.forEach(idxs => {
|
||||||
const [start, length] = idxs;
|
const [start, length] = idxs;
|
||||||
if (typeof start !== "number") return;
|
if (typeof start !== "number") return;
|
||||||
opName += this.name.slice(pos, start) + "<b>" +
|
opName += this.name.slice(pos, start) + "<strong>" +
|
||||||
this.name.slice(start, start + length) + "</b>";
|
this.name.slice(start, start + length) + "</strong>";
|
||||||
pos = start + length;
|
pos = start + length;
|
||||||
});
|
});
|
||||||
opName += this.name.slice(pos, this.name.length);
|
opName += this.name.slice(pos, this.name.length);
|
||||||
this.name = opName;
|
span.innerHTML = opName;
|
||||||
|
} else {
|
||||||
|
span.innerText = this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.description && descIdxs.length && descIdxs[0][0] >= 0) {
|
return span;
|
||||||
// Find HTML tag offsets
|
|
||||||
const re = /<[^>]+>/g;
|
|
||||||
let match;
|
|
||||||
while ((match = re.exec(this.description))) {
|
|
||||||
// If the search string occurs within an HTML tag, return without highlighting it.
|
|
||||||
const inHTMLTag = descIdxs.reduce((acc, idxs) => {
|
|
||||||
const start = idxs[0];
|
|
||||||
return start >= match.index && start <= (match.index + match[0].length);
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
if (inHTMLTag) return;
|
// if (this.description && descIdxs.length && descIdxs[0][0] >= 0) {
|
||||||
}
|
// // Find HTML tag offsets
|
||||||
|
// const re = /<[^>]+>/g;
|
||||||
let desc = "",
|
// let match;
|
||||||
pos = 0;
|
// while ((match = re.exec(this.description))) {
|
||||||
|
// // If the search string occurs within an HTML tag, return without highlighting it.
|
||||||
descIdxs.forEach(idxs => {
|
// const inHTMLTag = descIdxs.reduce((acc, idxs) => {
|
||||||
const [start, length] = idxs;
|
// const start = idxs[0];
|
||||||
desc += this.description.slice(pos, start) + "<b><u>" +
|
// return start >= match.index && start <= (match.index + match[0].length);
|
||||||
this.description.slice(start, start + length) + "</u></b>";
|
// }, false);
|
||||||
pos = start + length;
|
//
|
||||||
});
|
// if (inHTMLTag) return;
|
||||||
desc += this.description.slice(pos, this.description.length);
|
// }
|
||||||
this.description = desc;
|
//
|
||||||
}
|
// let desc = "",
|
||||||
|
// pos = 0;
|
||||||
|
//
|
||||||
|
// descIdxs.forEach(idxs => {
|
||||||
|
// const [start, length] = idxs;
|
||||||
|
// desc += this.description.slice(pos, start) + "<b><u>" +
|
||||||
|
// this.description.slice(start, start + length) + "</u></b>";
|
||||||
|
// pos = start + length;
|
||||||
|
// });
|
||||||
|
// desc += this.description.slice(pos, this.description.length);
|
||||||
|
// this.description = desc;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ import Sortable from "sortablejs";
|
||||||
* c(ustom element)-operation-list
|
* c(ustom element)-operation-list
|
||||||
*
|
*
|
||||||
* @param {App} app - The main view object for CyberChef
|
* @param {App} app - The main view object for CyberChef
|
||||||
* @param {string[]} opNames - A list of operation names
|
* @param {[string, number[]]} operations - A list of operation names and indexes of characters to highlight
|
||||||
* @param {boolean} includeStarIcon - optionally add the 'star' icon to the left of the operation
|
* @param {boolean} includeStarIcon - optionally add the 'star' icon to the left of the operation
|
||||||
* @param {Object} icon ( { class: string, innerText: string } ). check-icon by default
|
* @param {Object} icon ( { class: string, innerText: string } ). check-icon by default
|
||||||
*/
|
*/
|
||||||
export class COperationList extends HTMLElement {
|
export class COperationList extends HTMLElement {
|
||||||
constructor(
|
constructor(
|
||||||
app,
|
app,
|
||||||
opNames,
|
operations,
|
||||||
includeStarIcon,
|
includeStarIcon,
|
||||||
isSortable = false,
|
isSortable = false,
|
||||||
isCloneable = true,
|
isCloneable = true,
|
||||||
|
@ -21,7 +21,7 @@ export class COperationList extends HTMLElement {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.opNames = opNames;
|
this.operations = operations;
|
||||||
this.includeStarIcon = includeStarIcon;
|
this.includeStarIcon = includeStarIcon;
|
||||||
this.isSortable = isSortable;
|
this.isSortable = isSortable;
|
||||||
this.isCloneable = isCloneable;
|
this.isCloneable = isCloneable;
|
||||||
|
@ -35,7 +35,7 @@ export class COperationList extends HTMLElement {
|
||||||
const ul = document.createElement("ul");
|
const ul = document.createElement("ul");
|
||||||
ul.classList.add("op-list");
|
ul.classList.add("op-list");
|
||||||
|
|
||||||
this.opNames.forEach((opName => {
|
this.operations.forEach((([opName, charIndicesToHighlight]) => {
|
||||||
const cOpLi = new COperationLi(
|
const cOpLi = new COperationLi(
|
||||||
this.app,
|
this.app,
|
||||||
opName,
|
opName,
|
||||||
|
@ -43,7 +43,8 @@ export class COperationList extends HTMLElement {
|
||||||
class: this.icon ? this.icon.class : "check-icon",
|
class: this.icon ? this.icon.class : "check-icon",
|
||||||
innerText: this.icon ? this.icon.innerText : "check"
|
innerText: this.icon ? this.icon.innerText : "check"
|
||||||
},
|
},
|
||||||
this.includeStarIcon
|
this.includeStarIcon,
|
||||||
|
charIndicesToHighlight
|
||||||
);
|
);
|
||||||
|
|
||||||
ul.appendChild(cOpLi);
|
ul.appendChild(cOpLi);
|
||||||
|
|
|
@ -311,13 +311,13 @@ input.toggle-string {
|
||||||
|
|
||||||
.break .form-group * { color: var(--breakpoint-font-colour) !important; }
|
.break .form-group * { color: var(--breakpoint-font-colour) !important; }
|
||||||
|
|
||||||
li.operation.selected-op {
|
c-operation-li li.operation.focused-op {
|
||||||
/*color: var(--selected-operation-font-color) !important;*/
|
color: var(--focused-operation-font-color) !important;
|
||||||
background-color: var(--selected-operation-bg-colour) !important;
|
background-color: var(--focused-operation-bg-colour) !important;
|
||||||
/*border-color: var(--selected-operation-border-colour) !important;*/
|
border-color: var(--focused-operation-border-colour) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*.selected-op .form-group * { color: var(--selected-operation-font-color) !important; }*/
|
/*.focused-op .form-group * { color: var(--focused-operation-font-color) !important; }*/
|
||||||
|
|
||||||
.flow-control-op {
|
.flow-control-op {
|
||||||
color: var(--fc-operation-font-colour) !important;
|
color: var(--fc-operation-font-colour) !important;
|
||||||
|
|
|
@ -27,3 +27,4 @@
|
||||||
#rec-list li.sortable-chosen{
|
#rec-list li.sortable-chosen{
|
||||||
filter: brightness(0.8);
|
filter: brightness(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,11 +54,12 @@
|
||||||
--rec-list-operation-bg-colour: #dff0d8;
|
--rec-list-operation-bg-colour: #dff0d8;
|
||||||
--rec-list-operation-border-colour: #d3e8c0;
|
--rec-list-operation-border-colour: #d3e8c0;
|
||||||
|
|
||||||
--selected-operation-font-color: #c09853;
|
--focused-operation-font-color: #c09853;
|
||||||
--selected-operation-bg-colour: #d5ebf5;
|
--focused-operation-bg-colour: #fcf8e3;
|
||||||
--selected-operation-border-colour: #fbeed5;
|
--focused-operation-border-colour: #fbeed5;
|
||||||
|
|
||||||
|
--selected-operation-bg-colour: #d5ebf5;
|
||||||
|
|
||||||
/*mobile UI: selected operation checkmark*/
|
|
||||||
--checkmark-color: var(--op-list-operation-font-colour);
|
--checkmark-color: var(--op-list-operation-font-colour);
|
||||||
|
|
||||||
--breakpoint-font-colour: #b94a48;
|
--breakpoint-font-colour: #b94a48;
|
||||||
|
|
|
@ -50,9 +50,11 @@
|
||||||
--rec-list-operation-bg-colour: #252525;
|
--rec-list-operation-bg-colour: #252525;
|
||||||
--rec-list-operation-border-colour: #444;
|
--rec-list-operation-border-colour: #444;
|
||||||
|
|
||||||
--selected-operation-font-color: #c5c5c5;
|
--focused-operation-font-color: #c5c5c5;
|
||||||
|
--focused-operation-bg-colour: #475663;
|
||||||
|
--focused-operation-border-colour: #444;
|
||||||
|
|
||||||
--selected-operation-bg-colour: #3f3f3f;
|
--selected-operation-bg-colour: #3f3f3f;
|
||||||
--selected-operation-border-colour: #444;
|
|
||||||
|
|
||||||
--checkmark-color: #47d047;
|
--checkmark-color: #47d047;
|
||||||
|
|
||||||
|
|
|
@ -50,9 +50,9 @@
|
||||||
--rec-list-operation-bg-colour: purple;
|
--rec-list-operation-bg-colour: purple;
|
||||||
--rec-list-operation-border-colour: green;
|
--rec-list-operation-border-colour: green;
|
||||||
|
|
||||||
--selected-operation-font-color: white;
|
--focused-operation-font-color: white;
|
||||||
--selected-operation-bg-colour: pink;
|
--focused-operation-bg-colour: pink;
|
||||||
--selected-operation-border-colour: blue;
|
--focused-operation-border-colour: blue;
|
||||||
|
|
||||||
--breakpoint-font-colour: white;
|
--breakpoint-font-colour: white;
|
||||||
--breakpoint-bg-colour: red;
|
--breakpoint-bg-colour: red;
|
||||||
|
|
|
@ -69,9 +69,9 @@
|
||||||
--rec-list-operation-bg-colour: var(--base02);
|
--rec-list-operation-bg-colour: var(--base02);
|
||||||
--rec-list-operation-border-colour: var(--base01);
|
--rec-list-operation-border-colour: var(--base01);
|
||||||
|
|
||||||
--selected-operation-font-color: var(--base1);
|
--focused-operation-font-color: var(--base1);
|
||||||
--selected-operation-bg-colour: var(--base02);
|
--focused-operation-bg-colour: var(--base02);
|
||||||
--selected-operation-border-colour: var(--base01);
|
--focused-operation-border-colour: var(--base01);
|
||||||
|
|
||||||
--breakpoint-font-colour: var(--sol-red);
|
--breakpoint-font-colour: var(--sol-red);
|
||||||
--breakpoint-bg-colour: var(--base02);
|
--breakpoint-bg-colour: var(--base02);
|
||||||
|
|
|
@ -69,9 +69,9 @@
|
||||||
--rec-list-operation-bg-colour: var(--base2);
|
--rec-list-operation-bg-colour: var(--base2);
|
||||||
--rec-list-operation-border-colour: var(--base1);
|
--rec-list-operation-border-colour: var(--base1);
|
||||||
|
|
||||||
--selected-operation-font-color: var(--base01);
|
--focused-operation-font-color: var(--base01);
|
||||||
--selected-operation-bg-colour: var(--base2);
|
--focused-operation-bg-colour: var(--base2);
|
||||||
--selected-operation-border-colour: var(--base1);
|
--focused-operation-border-colour: var(--base1);
|
||||||
|
|
||||||
--breakpoint-font-colour: var(--sol-red);
|
--breakpoint-font-colour: var(--sol-red);
|
||||||
--breakpoint-bg-colour: var(--base2);
|
--breakpoint-bg-colour: var(--base2);
|
||||||
|
|
|
@ -34,8 +34,7 @@ class OperationsWaiter {
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
*/
|
*/
|
||||||
searchOperations(e) {
|
searchOperations(e) {
|
||||||
let ops, selected;
|
let ops, focused;
|
||||||
|
|
||||||
if (e.type === "keyup") {
|
if (e.type === "keyup") {
|
||||||
const searchResults = document.getElementById("search-results");
|
const searchResults = document.getElementById("search-results");
|
||||||
|
|
||||||
|
@ -46,13 +45,13 @@ class OperationsWaiter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.type === "search" || e.key === "Enter") { // Search or Return ( enter )
|
if (e.key === "Enter") { // Search or Return ( enter )
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
|
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
|
||||||
if (ops.length) {
|
if (ops.length) {
|
||||||
selected = this.getSelectedOp(ops);
|
focused = this.getFocusedOp(ops);
|
||||||
if (selected > -1) {
|
if (focused > -1) {
|
||||||
this.manager.recipe.addOperation(ops[selected].getAttribute("data-name"));
|
this.manager.recipe.addOperation(ops[focused].getAttribute("data-name"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,28 +59,28 @@ class OperationsWaiter {
|
||||||
if (e.type === "click" && !e.target.value.length) {
|
if (e.type === "click" && !e.target.value.length) {
|
||||||
this.openOpsDropdown();
|
this.openOpsDropdown();
|
||||||
} else if (e.key === "Escape") { // Escape
|
} else if (e.key === "Escape") { // Escape
|
||||||
this.closeOpsDropdown()
|
this.closeOpsDropdown();
|
||||||
} else if (e.key === "ArrowDown") { // Down
|
} else if (e.key === "ArrowDown") { // Down
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
|
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
|
||||||
if (ops.length) {
|
if (ops.length) {
|
||||||
selected = this.getSelectedOp(ops);
|
focused = this.getFocusedOp(ops);
|
||||||
if (selected > -1) {
|
if (focused > -1) {
|
||||||
ops[selected].classList.remove("selected-op");
|
ops[focused].classList.remove("focused-op");
|
||||||
}
|
}
|
||||||
if (selected === ops.length-1) selected = -1;
|
if (focused === ops.length-1) focused = -1;
|
||||||
ops[selected+1].classList.add("selected-op");
|
ops[focused+1].classList.add("focused-op");
|
||||||
}
|
}
|
||||||
} else if (e.key === "ArrowUp") { // Up
|
} else if (e.key === "ArrowUp") { // Up
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
|
ops = document.querySelectorAll("#search-results c-operation-list c-operation-li li");
|
||||||
if (ops.length) {
|
if (ops.length) {
|
||||||
selected = this.getSelectedOp(ops);
|
focused = this.getFocusedOp(ops);
|
||||||
if (selected > -1) {
|
if (focused > -1) {
|
||||||
ops[selected].classList.remove("selected-op");
|
ops[focused].classList.remove("focused-op");
|
||||||
}
|
}
|
||||||
if (selected === 0) selected = ops.length;
|
if (focused === 0) focused = ops.length;
|
||||||
ops[selected-1].classList.add("selected-op");
|
ops[focused-1].classList.add("focused-op");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const searchResultsEl = document.getElementById("search-results");
|
const searchResultsEl = document.getElementById("search-results");
|
||||||
|
@ -99,15 +98,10 @@ class OperationsWaiter {
|
||||||
|
|
||||||
if (str) {
|
if (str) {
|
||||||
const matchedOps = this.filterOperations(str, true);
|
const matchedOps = this.filterOperations(str, true);
|
||||||
let formattedOpNames = [];
|
|
||||||
|
|
||||||
matchedOps.forEach((operation) => {
|
|
||||||
formattedOpNames.push(operation.name.replace(/(<([^>]+)>)/ig, ""));
|
|
||||||
})
|
|
||||||
|
|
||||||
const cOpList = new COperationList(
|
const cOpList = new COperationList(
|
||||||
this.app,
|
this.app,
|
||||||
formattedOpNames,
|
matchedOps,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -132,15 +126,15 @@ class OperationsWaiter {
|
||||||
* @param {string} searchStr
|
* @param {string} searchStr
|
||||||
* @param {boolean} highlight - Whether to highlight the matching string in the operation
|
* @param {boolean} highlight - Whether to highlight the matching string in the operation
|
||||||
* name and description
|
* name and description
|
||||||
* @returns {string[]}
|
* @returns {[[string, number[]]]}
|
||||||
*/
|
*/
|
||||||
filterOperations(inStr, highlight) {
|
filterOperations(searchStr, highlight) {
|
||||||
const matchedOps = [];
|
const matchedOps = [];
|
||||||
const matchedDescs = [];
|
const matchedDescs = [];
|
||||||
|
|
||||||
// Create version with no whitespace for the fuzzy match
|
// Create version with no whitespace for the fuzzy match
|
||||||
// Helps avoid missing matches e.g. query "TCP " would not find "Parse TCP"
|
// Helps avoid missing matches e.g. query "TCP " would not find "Parse TCP"
|
||||||
const inStrNWS = inStr.replace(/\s/g, "");
|
const inStrNWS = searchStr.replace(/\s/g, "");
|
||||||
|
|
||||||
for (const opName in this.app.operations) {
|
for (const opName in this.app.operations) {
|
||||||
const op = this.app.operations[opName];
|
const op = this.app.operations[opName];
|
||||||
|
@ -149,26 +143,13 @@ class OperationsWaiter {
|
||||||
const [nameMatch, score, idxs] = fuzzyMatch(inStrNWS, opName);
|
const [nameMatch, score, idxs] = fuzzyMatch(inStrNWS, opName);
|
||||||
|
|
||||||
// Match description based on exact match
|
// Match description based on exact match
|
||||||
const descPos = op.description.toLowerCase().indexOf(inStr.toLowerCase());
|
const descPos = op.description.toLowerCase().indexOf(searchStr.toLowerCase());
|
||||||
|
|
||||||
if (nameMatch || descPos >= 0) {
|
if (nameMatch || descPos >= 0) {
|
||||||
const operation = new COperationLi(
|
|
||||||
this.app,
|
|
||||||
opName,
|
|
||||||
{
|
|
||||||
class: "check-icon",
|
|
||||||
innerText: "check"
|
|
||||||
},
|
|
||||||
true );
|
|
||||||
|
|
||||||
if (highlight) {
|
|
||||||
operation.highlightSearchStrings(calcMatchRanges(idxs), [[descPos, inStr.length]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nameMatch) {
|
if (nameMatch) {
|
||||||
matchedOps.push([operation, score]);
|
matchedOps.push([[opName, calcMatchRanges(idxs)], score]);
|
||||||
} else {
|
} else {
|
||||||
matchedDescs.push(operation);
|
matchedDescs.push([opName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,15 +162,15 @@ class OperationsWaiter {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the operation which has been selected using keyboard shortcuts. This will have the class
|
* Finds the operation which has been focused on using keyboard shortcuts. This will have the class
|
||||||
* 'selected-op' set. Returns the index of the operation within the given list.
|
* 'focused-op' set. Returns the index of the operation within the given list.
|
||||||
*
|
*
|
||||||
* @param {element[]} ops
|
* @param {element[]} ops
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
getSelectedOp(ops) {
|
getFocusedOp(ops) {
|
||||||
for (let i = 0; i < ops.length; i++) {
|
for (let i = 0; i < ops.length; i++) {
|
||||||
if (ops[i].classList.contains("selected-op")) {
|
if (ops[i].classList.contains("focused-op")) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +197,7 @@ class OperationsWaiter {
|
||||||
if(favCatConfig !== undefined) {
|
if(favCatConfig !== undefined) {
|
||||||
const opList = new COperationList(
|
const opList = new COperationList(
|
||||||
this.app,
|
this.app,
|
||||||
favCatConfig.ops,
|
favCatConfig.ops.map( op => [op]),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue