mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-09 07:45:00 -04:00
ccessible user experience
This commit is contained in:
parent
7c66dacc40
commit
eb04145a62
9 changed files with 127 additions and 6 deletions
|
@ -48,7 +48,7 @@
|
||||||
"autoprefixer": "^10.4.4",
|
"autoprefixer": "^10.4.4",
|
||||||
"babel-loader": "^8.2.4",
|
"babel-loader": "^8.2.4",
|
||||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||||
"chromedriver": "^99.0.0",
|
"chromedriver": "^101.0.0",
|
||||||
"cli-progress": "^3.10.0",
|
"cli-progress": "^3.10.0",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
"copy-webpack-plugin": "^10.2.4",
|
||||||
|
|
|
@ -46,7 +46,7 @@ class HTMLOperation {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
toStubHtml(removeIcon) {
|
toStubHtml(removeIcon) {
|
||||||
let html = "<li class='operation'";
|
let html = `<li class='operation' data-opName="${this.name}"`;
|
||||||
|
|
||||||
if (this.description) {
|
if (this.description) {
|
||||||
const infoLink = this.infoURL ? `<hr>${titleFromWikiLink(this.infoURL)}` : "";
|
const infoLink = this.infoURL ? `<hr>${titleFromWikiLink(this.infoURL)}` : "";
|
||||||
|
@ -58,6 +58,12 @@ class HTMLOperation {
|
||||||
|
|
||||||
html += ">" + this.name;
|
html += ">" + this.name;
|
||||||
|
|
||||||
|
html += `<span class='float-right'>
|
||||||
|
<button type="button" class="btn btn-primary bmd-btn-icon accessibleUX" data-toggle="tooltip" data-original-title="Add to recipe">
|
||||||
|
<i class='material-icons'>add_box</i>
|
||||||
|
</button>
|
||||||
|
</span>`;
|
||||||
|
|
||||||
if (removeIcon) {
|
if (removeIcon) {
|
||||||
html += "<i class='material-icons remove-icon op-icon'>delete</i>";
|
html += "<i class='material-icons remove-icon op-icon'>delete</i>";
|
||||||
}
|
}
|
||||||
|
@ -83,6 +89,9 @@ class HTMLOperation {
|
||||||
|
|
||||||
html += `</div>
|
html += `</div>
|
||||||
<div class="recip-icons">
|
<div class="recip-icons">
|
||||||
|
<i class="material-icons move-down accessibleUX" title="Move down">arrow_downward</i>
|
||||||
|
<i class="material-icons move-up accessibleUX">arrow_upward</i>
|
||||||
|
<i class="material-icons remove-icon accessibleUX" title="Delete operation">delete</i>
|
||||||
<i class="material-icons breakpoint" title="Set breakpoint" break="false">pause</i>
|
<i class="material-icons breakpoint" title="Set breakpoint" break="false">pause</i>
|
||||||
<i class="material-icons disable-icon" title="Disable operation" disabled="false">not_interested</i>
|
<i class="material-icons disable-icon" title="Disable operation" disabled="false">not_interested</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -93,6 +93,7 @@ class Manager {
|
||||||
this.bindings.updateKeybList();
|
this.bindings.updateKeybList();
|
||||||
this.background.registerChefWorker();
|
this.background.registerChefWorker();
|
||||||
this.seasonal.load();
|
this.seasonal.load();
|
||||||
|
this.options.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,6 +127,7 @@ class Manager {
|
||||||
// Operations
|
// Operations
|
||||||
this.addMultiEventListener("#search", "keyup paste search", this.ops.searchOperations, this.ops);
|
this.addMultiEventListener("#search", "keyup paste search", this.ops.searchOperations, this.ops);
|
||||||
this.addDynamicListener(".op-list li.operation", "dblclick", this.ops.operationDblclick, this.ops);
|
this.addDynamicListener(".op-list li.operation", "dblclick", this.ops.operationDblclick, this.ops);
|
||||||
|
this.addDynamicListener(".op-list li.operation button i", "click", this.ops.operationAdd, this.ops);
|
||||||
document.getElementById("edit-favourites").addEventListener("click", this.ops.editFavouritesClick.bind(this.ops));
|
document.getElementById("edit-favourites").addEventListener("click", this.ops.editFavouritesClick.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));
|
||||||
|
@ -137,6 +139,9 @@ class Manager {
|
||||||
this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe);
|
this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe);
|
||||||
this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe);
|
this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe);
|
||||||
this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe);
|
this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe);
|
||||||
|
this.addDynamicListener(".remove-icon", "click", this.recipe.removeClick, this.recipe);
|
||||||
|
this.addDynamicListener(".move-down", "click", this.recipe.moveDownClick, this.recipe);
|
||||||
|
this.addDynamicListener(".move-up", "click", this.recipe.moveUpClick, this.recipe);
|
||||||
this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe);
|
this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe);
|
||||||
this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe);
|
this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe);
|
||||||
this.addDynamicListener("#rec-list .dropdown-menu.toggle-dropdown a", "click", this.recipe.dropdownToggleClick, this.recipe);
|
this.addDynamicListener("#rec-list .dropdown-menu.toggle-dropdown a", "click", this.recipe.dropdownToggleClick, this.recipe);
|
||||||
|
@ -233,6 +238,7 @@ class Manager {
|
||||||
document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options));
|
document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options));
|
||||||
document.getElementById("logLevel").addEventListener("change", this.options.logLevelChange.bind(this.options));
|
document.getElementById("logLevel").addEventListener("change", this.options.logLevelChange.bind(this.options));
|
||||||
document.getElementById("imagePreview").addEventListener("change", this.input.renderFileThumb.bind(this.input));
|
document.getElementById("imagePreview").addEventListener("change", this.input.renderFileThumb.bind(this.input));
|
||||||
|
document.getElementById("accessibleUX").addEventListener("change", this.options.uxChange.bind(this.options));
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
window.addEventListener("keydown", this.bindings.parseInput.bind(this.bindings));
|
window.addEventListener("keydown", this.bindings.parseInput.bind(this.bindings));
|
||||||
|
|
|
@ -584,6 +584,13 @@
|
||||||
Keep the current tab in sync between the input and output
|
Keep the current tab in sync between the input and output
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="checkbox option-item">
|
||||||
|
<label for="accessibleUX">
|
||||||
|
<input type="checkbox" option="accessibleUX" id="accessibleUX">
|
||||||
|
Accessible user experience, buttons for drag-and-drop operations
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button>
|
<button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button>
|
||||||
|
|
|
@ -54,7 +54,8 @@ function main() {
|
||||||
autoMagic: true,
|
autoMagic: true,
|
||||||
imagePreview: true,
|
imagePreview: true,
|
||||||
syncTabs: true,
|
syncTabs: true,
|
||||||
preserveCR: "entropy"
|
preserveCR: "entropy",
|
||||||
|
accessibleUX: false
|
||||||
};
|
};
|
||||||
|
|
||||||
document.removeEventListener("DOMContentLoaded", main, false);
|
document.removeEventListener("DOMContentLoaded", main, false);
|
||||||
|
|
|
@ -205,8 +205,21 @@ class OperationsWaiter {
|
||||||
*/
|
*/
|
||||||
operationDblclick(e) {
|
operationDblclick(e) {
|
||||||
const li = e.target;
|
const li = e.target;
|
||||||
|
// get operation name from <li> data
|
||||||
|
this.manager.recipe.addOperation($(li).data("opname"));
|
||||||
|
}
|
||||||
|
|
||||||
this.manager.recipe.addOperation(li.textContent);
|
/**
|
||||||
|
* Handler for operation add events.
|
||||||
|
* Adds the operation to the recipe and auto bakes.
|
||||||
|
*
|
||||||
|
* @param {event} e
|
||||||
|
*/
|
||||||
|
operationAdd(e) {
|
||||||
|
log.info("add");
|
||||||
|
const li = e.target.parentNode.parentNode.parentNode;
|
||||||
|
// get operation name from <li> data
|
||||||
|
this.manager.recipe.addOperation($(li).data("opname"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,8 @@ class OptionsWaiter {
|
||||||
for (i = 0; i < cboxes.length; i++) {
|
for (i = 0; i < cboxes.length; i++) {
|
||||||
cboxes[i].checked = this.app.options[cboxes[i].getAttribute("option")];
|
cboxes[i].checked = this.app.options[cboxes[i].getAttribute("option")];
|
||||||
}
|
}
|
||||||
|
// init ux option - ux is last cbox
|
||||||
|
cboxes[i-1].dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
const nboxes = document.querySelectorAll("#options-body input[type=number]");
|
const nboxes = document.querySelectorAll("#options-body input[type=number]");
|
||||||
for (i = 0; i < nboxes.length; i++) {
|
for (i = 0; i < nboxes.length; i++) {
|
||||||
|
@ -189,6 +191,18 @@ class OptionsWaiter {
|
||||||
this.manager.worker.setLogLevel();
|
this.manager.worker.setLogLevel();
|
||||||
this.manager.input.setLogLevel();
|
this.manager.input.setLogLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the UX using CSS change so that actions can be managed
|
||||||
|
* regardless of visibility
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
uxChange(e) {
|
||||||
|
const checked = $("#accessibleUX").is(":checked");
|
||||||
|
$(".accessibleUX").css("display", checked ? "block" : "none");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OptionsWaiter;
|
export default OptionsWaiter;
|
||||||
|
|
|
@ -261,6 +261,43 @@ class RecipeWaiter {
|
||||||
window.dispatchEvent(this.manager.statechange);
|
window.dispatchEvent(this.manager.statechange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for remove click events.
|
||||||
|
* Removes the operation from the recipe and auto bakes.
|
||||||
|
*
|
||||||
|
* @fires Manager#statechange
|
||||||
|
* @param {event} e
|
||||||
|
*/
|
||||||
|
removeClick(e) {
|
||||||
|
e.target.parentNode.parentNode.remove();
|
||||||
|
this.opRemove(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for remove click events.
|
||||||
|
* Removes the operation from the recipe and auto bakes.
|
||||||
|
*
|
||||||
|
* @fires Manager#statechange
|
||||||
|
* @param {event} e
|
||||||
|
*/
|
||||||
|
moveUpClick(e) {
|
||||||
|
const li = $(e.target.parentNode.parentNode);
|
||||||
|
li.prev().before(li);
|
||||||
|
window.dispatchEvent(this.manager.statechange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for down click events.
|
||||||
|
* Moves the operation in the recipe and auto bakes.
|
||||||
|
*
|
||||||
|
* @fires Manager#statechange
|
||||||
|
* @param {event} e
|
||||||
|
*/
|
||||||
|
moveDownClick(e) {
|
||||||
|
const li = $(e.target.parentNode.parentNode);
|
||||||
|
li.next().after(li);
|
||||||
|
window.dispatchEvent(this.manager.statechange);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for operation doubleclick events.
|
* Handler for operation doubleclick events.
|
||||||
|
@ -368,7 +405,7 @@ class RecipeWaiter {
|
||||||
* @param {element} el - The operation stub element from the operations pane
|
* @param {element} el - The operation stub element from the operations pane
|
||||||
*/
|
*/
|
||||||
buildRecipeOperation(el) {
|
buildRecipeOperation(el) {
|
||||||
const opName = el.textContent;
|
const opName = $(el).data('opname');
|
||||||
const op = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
|
const op = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
|
||||||
el.innerHTML = op.toFullHtml();
|
el.innerHTML = op.toFullHtml();
|
||||||
|
|
||||||
|
@ -395,7 +432,7 @@ class RecipeWaiter {
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
|
|
||||||
item.classList.add("operation");
|
item.classList.add("operation");
|
||||||
item.innerHTML = name;
|
$(item).data("opname", name);
|
||||||
this.buildRecipeOperation(item);
|
this.buildRecipeOperation(item);
|
||||||
document.getElementById("rec-list").appendChild(item);
|
document.getElementById("rec-list").appendChild(item);
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,40 @@ module.exports = {
|
||||||
browser.expect.element("//li[contains(@class, 'operation') and text()='Register']").to.be.present;
|
browser.expect.element("//li[contains(@class, 'operation') and text()='Register']").to.be.present;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"Accessible user experience": browser => {
|
||||||
|
const addOp = "#catFavourites li.operation";
|
||||||
|
const recOp = "#rec-list li:nth-child(1)";
|
||||||
|
const down = " i.move-down.accessibleUX";
|
||||||
|
const remove = " i.remove-icon.accessibleUX";
|
||||||
|
|
||||||
|
// Switch UX on
|
||||||
|
browser
|
||||||
|
.useCss()
|
||||||
|
.click("#options")
|
||||||
|
.waitForElementVisible("#options-modal", 1000)
|
||||||
|
.click("#reset-options")
|
||||||
|
.pause(500)
|
||||||
|
// Using label for checkbox click because nightwatch thinks #acessibleUX isn't visible
|
||||||
|
.click('label[for="accessibleUX"]')
|
||||||
|
.click("#options-modal .modal-footer button:last-child")
|
||||||
|
.waitForElementNotVisible("#options-modal")
|
||||||
|
.expect.element(addOp).to.be.visible;
|
||||||
|
|
||||||
|
// add Operations & move them
|
||||||
|
browser
|
||||||
|
.useCss()
|
||||||
|
.click(addOp + ":nth-child(1) button")
|
||||||
|
.click(addOp + ":nth-child(2) button")
|
||||||
|
.click(recOp + down)
|
||||||
|
.expect.element(recOp).text.to.contain("From Base64");
|
||||||
|
|
||||||
|
// delete operations
|
||||||
|
browser
|
||||||
|
.click(recOp + remove)
|
||||||
|
.click(recOp + remove)
|
||||||
|
.waitForElementNotPresent(recOp);
|
||||||
|
},
|
||||||
|
|
||||||
"Recipe can be run": browser => {
|
"Recipe can be run": browser => {
|
||||||
const toHex = "//li[contains(@class, 'operation') and text()='To Hex']";
|
const toHex = "//li[contains(@class, 'operation') and text()='To Hex']";
|
||||||
const op = "#rec-list .operation .op-title";
|
const op = "#rec-list .operation .op-title";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue