mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-23 08:16:17 -04:00
statusbar popup keyboard navigation
This commit is contained in:
parent
85a3510454
commit
6fcf103760
3 changed files with 135 additions and 126 deletions
|
@ -478,6 +478,11 @@
|
||||||
background-color: #ddd
|
background-color: #ddd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Change color of dropup links on focus */
|
||||||
|
.cm-status-bar-select-content a:focus {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
/* Change the background color of the dropup button when the dropup content is shown */
|
/* Change the background color of the dropup button when the dropup content is shown */
|
||||||
.cm-status-bar-select:hover .cm-status-bar-select-btn {
|
.cm-status-bar-select:hover .cm-status-bar-select-btn {
|
||||||
background-color: #f1f1f1;
|
background-color: #f1f1f1;
|
||||||
|
|
|
@ -4,11 +4,8 @@
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { showPanel } from "@codemirror/view";
|
import {showPanel} from "@codemirror/view";
|
||||||
import {
|
import {CHR_ENC_SIMPLE_LOOKUP, CHR_ENC_SIMPLE_REVERSE_LOOKUP} from "../../core/lib/ChrEnc.mjs";
|
||||||
CHR_ENC_SIMPLE_LOOKUP,
|
|
||||||
CHR_ENC_SIMPLE_REVERSE_LOOKUP,
|
|
||||||
} from "../../core/lib/ChrEnc.mjs";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Status bar extension for CodeMirror
|
* A Status bar extension for CodeMirror
|
||||||
|
@ -44,10 +41,7 @@ class StatusBarPanel {
|
||||||
|
|
||||||
dom.className = "cm-status-bar";
|
dom.className = "cm-status-bar";
|
||||||
dom.setAttribute("data-help-title", `${this.label} status bar`);
|
dom.setAttribute("data-help-title", `${this.label} status bar`);
|
||||||
dom.setAttribute(
|
dom.setAttribute("data-help", `This status bar provides information about data in the ${this.label}. Help topics are available for each of the components by activating help when hovering over them.`);
|
||||||
"data-help",
|
|
||||||
`This status bar provides information about data in the ${this.label}. Help topics are available for each of the components by activating help when hovering over them.`
|
|
||||||
);
|
|
||||||
lhs.innerHTML = this.constructLHS();
|
lhs.innerHTML = this.constructLHS();
|
||||||
rhs.innerHTML = this.constructRHS();
|
rhs.innerHTML = this.constructRHS();
|
||||||
|
|
||||||
|
@ -58,25 +52,27 @@ class StatusBarPanel {
|
||||||
const eventHandler = this.showDropUp.bind(this);
|
const eventHandler = this.showDropUp.bind(this);
|
||||||
|
|
||||||
dom.querySelectorAll(".cm-status-bar-select-btn").forEach((el) => {
|
dom.querySelectorAll(".cm-status-bar-select-btn").forEach((el) => {
|
||||||
el.addEventListener("click", eventHandler, false),
|
el.addEventListener("click", eventHandler, false);
|
||||||
|
});
|
||||||
|
dom.querySelectorAll(".cm-status-bar-select-btn").forEach((el) => {
|
||||||
el.addEventListener("keydown", eventHandler, false);
|
el.addEventListener("keydown", eventHandler, false);
|
||||||
});
|
});
|
||||||
dom.querySelector(".eol-select").addEventListener(
|
|
||||||
"click",
|
const selectContent = dom.querySelectorAll(
|
||||||
this.eolSelectClick.bind(this),
|
".cm-status-bar-select-content"
|
||||||
false
|
|
||||||
);
|
|
||||||
dom.querySelector(".chr-enc-select").addEventListener(
|
|
||||||
"click",
|
|
||||||
this.chrEncSelectClick.bind(this),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
dom.querySelector(".cm-status-bar-filter-input").addEventListener(
|
|
||||||
"keyup",
|
|
||||||
this.chrEncFilter.bind(this),
|
|
||||||
false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
selectContent.forEach((el) => {
|
||||||
|
const aTags = el.getElementsByTagName("a");
|
||||||
|
|
||||||
|
for (let i = 0; i < aTags.length; i++) {
|
||||||
|
aTags[i].addEventListener("keydown", arrowNav, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dom.querySelector(".eol-select").addEventListener("click", this.eolSelectClick.bind(this), false);
|
||||||
|
dom.querySelector(".chr-enc-select").addEventListener("click", this.chrEncSelectClick.bind(this), false);
|
||||||
|
dom.querySelector(".cm-status-bar-filter-input").addEventListener("keyup", this.chrEncFilter.bind(this), false);
|
||||||
|
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,14 +113,14 @@ class StatusBarPanel {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const eolLookup = {
|
const eolLookup = {
|
||||||
LF: "\u000a",
|
"LF": "\u000a",
|
||||||
VT: "\u000b",
|
"VT": "\u000b",
|
||||||
FF: "\u000c",
|
"FF": "\u000c",
|
||||||
CR: "\u000d",
|
"CR": "\u000d",
|
||||||
CRLF: "\u000d\u000a",
|
"CRLF": "\u000d\u000a",
|
||||||
NEL: "\u0085",
|
"NEL": "\u0085",
|
||||||
LS: "\u2028",
|
"LS": "\u2028",
|
||||||
PS: "\u2029",
|
"PS": "\u2029"
|
||||||
};
|
};
|
||||||
const eolval = eolLookup[e.target.getAttribute("data-val")];
|
const eolval = eolLookup[e.target.getAttribute("data-val")];
|
||||||
|
|
||||||
|
@ -198,9 +194,9 @@ class StatusBarPanel {
|
||||||
* @param {boolean} selectionSet
|
* @param {boolean} selectionSet
|
||||||
*/
|
*/
|
||||||
updateSelection(state, selectionSet) {
|
updateSelection(state, selectionSet) {
|
||||||
const selLen = state?.selection?.main
|
const selLen = state?.selection?.main ?
|
||||||
? state.selection.main.to - state.selection.main.from
|
state.selection.main.to - state.selection.main.from :
|
||||||
: 0;
|
0;
|
||||||
|
|
||||||
const selInfo = this.dom.querySelector(".sel-info"),
|
const selInfo = this.dom.querySelector(".sel-info"),
|
||||||
curOffsetInfo = this.dom.querySelector(".cur-offset-info");
|
curOffsetInfo = this.dom.querySelector(".cur-offset-info");
|
||||||
|
@ -218,12 +214,11 @@ class StatusBarPanel {
|
||||||
if (state.lineBreak.length !== 1) {
|
if (state.lineBreak.length !== 1) {
|
||||||
const fromLine = state.doc.lineAt(from).number;
|
const fromLine = state.doc.lineAt(from).number;
|
||||||
const toLine = state.doc.lineAt(to).number;
|
const toLine = state.doc.lineAt(to).number;
|
||||||
from += state.lineBreak.length * fromLine - fromLine - 1;
|
from += (state.lineBreak.length * fromLine) - fromLine - 1;
|
||||||
to += state.lineBreak.length * toLine - toLine - 1;
|
to += (state.lineBreak.length * toLine) - toLine - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selLen > 0) {
|
if (selLen > 0) { // Range
|
||||||
// Range
|
|
||||||
const start = this.dom.querySelector(".sel-start-value"),
|
const start = this.dom.querySelector(".sel-start-value"),
|
||||||
end = this.dom.querySelector(".sel-end-value"),
|
end = this.dom.querySelector(".sel-end-value"),
|
||||||
length = this.dom.querySelector(".sel-length-value");
|
length = this.dom.querySelector(".sel-length-value");
|
||||||
|
@ -233,8 +228,7 @@ class StatusBarPanel {
|
||||||
start.textContent = from;
|
start.textContent = from;
|
||||||
end.textContent = to;
|
end.textContent = to;
|
||||||
length.textContent = to - from;
|
length.textContent = to - from;
|
||||||
} else {
|
} else { // Position
|
||||||
// Position
|
|
||||||
const offset = this.dom.querySelector(".cur-offset-value");
|
const offset = this.dom.querySelector(".cur-offset-value");
|
||||||
|
|
||||||
selInfo.style.display = "none";
|
selInfo.style.display = "none";
|
||||||
|
@ -258,7 +252,7 @@ class StatusBarPanel {
|
||||||
"\u000d\u000a": ["CRLF", "Carriage Return + Line Feed"],
|
"\u000d\u000a": ["CRLF", "Carriage Return + Line Feed"],
|
||||||
"\u0085": ["NEL", "Next Line"],
|
"\u0085": ["NEL", "Next Line"],
|
||||||
"\u2028": ["LS", "Line Separator"],
|
"\u2028": ["LS", "Line Separator"],
|
||||||
"\u2029": ["PS", "Paragraph Separator"],
|
"\u2029": ["PS", "Paragraph Separator"]
|
||||||
};
|
};
|
||||||
|
|
||||||
const val = this.dom.querySelector(".eol-value");
|
const val = this.dom.querySelector(".eol-value");
|
||||||
|
@ -266,10 +260,7 @@ class StatusBarPanel {
|
||||||
const eolName = eolLookup[state.lineBreak];
|
const eolName = eolLookup[state.lineBreak];
|
||||||
val.textContent = eolName[0];
|
val.textContent = eolName[0];
|
||||||
button.setAttribute("title", `End of line sequence:<br>${eolName[1]}`);
|
button.setAttribute("title", `End of line sequence:<br>${eolName[1]}`);
|
||||||
button.setAttribute(
|
button.setAttribute("data-original-title", `End of line sequence:<br>${eolName[1]}`);
|
||||||
"data-original-title",
|
|
||||||
`End of line sequence:<br>${eolName[1]}`
|
|
||||||
);
|
|
||||||
this.eolVal = state.lineBreak;
|
this.eolVal = state.lineBreak;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,21 +271,13 @@ class StatusBarPanel {
|
||||||
const chrEncVal = this.chrEncGetter();
|
const chrEncVal = this.chrEncGetter();
|
||||||
if (chrEncVal === this.chrEncVal) return;
|
if (chrEncVal === this.chrEncVal) return;
|
||||||
|
|
||||||
const name = CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal]
|
const name = CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] ? CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] : "Raw Bytes";
|
||||||
? CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal]
|
|
||||||
: "Raw Bytes";
|
|
||||||
|
|
||||||
const val = this.dom.querySelector(".chr-enc-value");
|
const val = this.dom.querySelector(".chr-enc-value");
|
||||||
const button = val.closest(".cm-status-bar-select-btn");
|
const button = val.closest(".cm-status-bar-select-btn");
|
||||||
val.textContent = name;
|
val.textContent = name;
|
||||||
button.setAttribute(
|
button.setAttribute("title", `${this.label} character encoding:<br>${name}`);
|
||||||
"title",
|
button.setAttribute("data-original-title", `${this.label} character encoding:<br>${name}`);
|
||||||
`${this.label} character encoding:<br>${name}`
|
|
||||||
);
|
|
||||||
button.setAttribute(
|
|
||||||
"data-original-title",
|
|
||||||
`${this.label} character encoding:<br>${name}`
|
|
||||||
);
|
|
||||||
this.chrEncVal = chrEncVal;
|
this.chrEncVal = chrEncVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,9 +294,7 @@ class StatusBarPanel {
|
||||||
bakingTimeInfo.style.display = "inline-block";
|
bakingTimeInfo.style.display = "inline-block";
|
||||||
bakingTime.textContent = this.timing.duration(this.tabNumGetter());
|
bakingTime.textContent = this.timing.duration(this.tabNumGetter());
|
||||||
|
|
||||||
const info = this.timing
|
const info = this.timing.printStages(this.tabNumGetter()).replace(/\n/g, "<br>");
|
||||||
.printStages(this.tabNumGetter())
|
|
||||||
.replace(/\n/g, "<br>");
|
|
||||||
bakingTimeInfo.setAttribute("data-original-title", info);
|
bakingTimeInfo.setAttribute("data-original-title", info);
|
||||||
} else {
|
} else {
|
||||||
bakingTimeInfo.style.display = "none";
|
bakingTimeInfo.style.display = "none";
|
||||||
|
@ -326,11 +307,11 @@ class StatusBarPanel {
|
||||||
*/
|
*/
|
||||||
updateSizing(view) {
|
updateSizing(view) {
|
||||||
const viewHeight = view.contentDOM.parentNode.clientHeight;
|
const viewHeight = view.contentDOM.parentNode.clientHeight;
|
||||||
this.dom
|
this.dom.querySelectorAll(".cm-status-bar-select-scroll").forEach(
|
||||||
.querySelectorAll(".cm-status-bar-select-scroll")
|
el => {
|
||||||
.forEach((el) => {
|
el.style.maxHeight = (viewHeight - 50) + "px";
|
||||||
el.style.maxHeight = viewHeight - 50 + "px";
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,27 +322,19 @@ class StatusBarPanel {
|
||||||
|
|
||||||
if (this.htmlOutput?.html === "") {
|
if (this.htmlOutput?.html === "") {
|
||||||
// Enable all controls
|
// Enable all controls
|
||||||
this.dom.querySelectorAll(".disabled").forEach((el) => {
|
this.dom.querySelectorAll(".disabled").forEach(el => {
|
||||||
el.classList.remove("disabled");
|
el.classList.remove("disabled");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Disable chrenc, length, selection etc.
|
// Disable chrenc, length, selection etc.
|
||||||
this.dom
|
this.dom.querySelectorAll(".cm-status-bar-select-btn").forEach(el => {
|
||||||
.querySelectorAll(".cm-status-bar-select-btn")
|
|
||||||
.forEach((el) => {
|
|
||||||
el.classList.add("disabled");
|
el.classList.add("disabled");
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dom
|
this.dom.querySelector(".stats-length-value").parentNode.classList.add("disabled");
|
||||||
.querySelector(".stats-length-value")
|
this.dom.querySelector(".stats-lines-value").parentNode.classList.add("disabled");
|
||||||
.parentNode.classList.add("disabled");
|
|
||||||
this.dom
|
|
||||||
.querySelector(".stats-lines-value")
|
|
||||||
.parentNode.classList.add("disabled");
|
|
||||||
this.dom.querySelector(".sel-info").classList.add("disabled");
|
this.dom.querySelector(".sel-info").classList.add("disabled");
|
||||||
this.dom
|
this.dom.querySelector(".cur-offset-info").classList.add("disabled");
|
||||||
.querySelector(".cur-offset-info")
|
|
||||||
.classList.add("disabled");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,25 +371,18 @@ class StatusBarPanel {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
constructRHS() {
|
constructRHS() {
|
||||||
const chrEncOptions = Object.keys(CHR_ENC_SIMPLE_LOOKUP)
|
const chrEncOptions = Object.keys(CHR_ENC_SIMPLE_LOOKUP).map(name =>
|
||||||
.map(
|
|
||||||
(name) =>
|
|
||||||
`<a href="#" draggable="false" data-val="${CHR_ENC_SIMPLE_LOOKUP[name]}">${name}</a>`
|
`<a href="#" draggable="false" data-val="${CHR_ENC_SIMPLE_LOOKUP[name]}">${name}</a>`
|
||||||
)
|
).join("");
|
||||||
.join("");
|
|
||||||
|
|
||||||
let chrEncHelpText = "",
|
let chrEncHelpText = "",
|
||||||
eolHelpText = "";
|
eolHelpText = "";
|
||||||
if (this.label === "Input") {
|
if (this.label === "Input") {
|
||||||
chrEncHelpText =
|
chrEncHelpText = "The input character encoding defines how the input text is encoded into bytes which are then processed by the Recipe.<br><br>The 'Raw bytes' option attempts to treat the input as individual bytes in the range 0-255. If it detects any characters with Unicode values above 255, it will treat the entire input as UTF-8. 'Raw bytes' is usually the best option if you are inputting binary data, such as a file.";
|
||||||
"The input character encoding defines how the input text is encoded into bytes which are then processed by the Recipe.<br><br>The 'Raw bytes' option attempts to treat the input as individual bytes in the range 0-255. If it detects any characters with Unicode values above 255, it will treat the entire input as UTF-8. 'Raw bytes' is usually the best option if you are inputting binary data, such as a file.";
|
eolHelpText = "The End of Line Sequence defines which bytes are considered EOL terminators. Pressing the return key will enter this value into the input and create a new line.<br><br>Changing the EOL sequence will not modify any existing data in the input but may change how previously entered line breaks are displayed. Lines added while a different EOL terminator was set may not now result in a new line, but may be displayed as control characters instead.";
|
||||||
eolHelpText =
|
|
||||||
"The End of Line Sequence defines which bytes are considered EOL terminators. Pressing the return key will enter this value into the input and create a new line.<br><br>Changing the EOL sequence will not modify any existing data in the input but may change how previously entered line breaks are displayed. Lines added while a different EOL terminator was set may not now result in a new line, but may be displayed as control characters instead.";
|
|
||||||
} else {
|
} else {
|
||||||
chrEncHelpText =
|
chrEncHelpText = "The output character encoding defines how the output bytes are decoded into text which can be displayed to you.<br><br>The 'Raw bytes' option treats the output data as individual bytes in the range 0-255.";
|
||||||
"The output character encoding defines how the output bytes are decoded into text which can be displayed to you.<br><br>The 'Raw bytes' option treats the output data as individual bytes in the range 0-255.";
|
eolHelpText = "The End of Line Sequence defines which bytes are considered EOL terminators.<br><br>Changing this value will not modify the value of the output, but may change how certain bytes are displayed and whether they result in a new line being created.";
|
||||||
eolHelpText =
|
|
||||||
"The End of Line Sequence defines which bytes are considered EOL terminators.<br><br>Changing this value will not modify the value of the output, but may change how certain bytes are displayed and whether they result in a new line being created.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
@ -431,7 +397,7 @@ class StatusBarPanel {
|
||||||
</span>
|
</span>
|
||||||
<div class="cm-status-bar-select-content">
|
<div class="cm-status-bar-select-content">
|
||||||
<div class="cm-status-bar-select-scroll no-select">
|
<div class="cm-status-bar-select-scroll no-select">
|
||||||
<a href="#" draggable="false" data-val="0">Raw Bytes</a>
|
<a href="#" draggable="false" data-val="0" tabindex="0">Raw Bytes</a>
|
||||||
${chrEncOptions}
|
${chrEncOptions}
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group cm-status-bar-filter-search">
|
<div class="input-group cm-status-bar-filter-search">
|
||||||
|
@ -449,15 +415,15 @@ class StatusBarPanel {
|
||||||
<span class="cm-status-bar-select-btn" data-toggle="tooltip" tabindex="0" data-html="true" data-placement="left" title="End of line sequence">
|
<span class="cm-status-bar-select-btn" data-toggle="tooltip" tabindex="0" data-html="true" data-placement="left" title="End of line sequence">
|
||||||
<i class="material-icons">keyboard_return</i> <span class="eol-value"></span>
|
<i class="material-icons">keyboard_return</i> <span class="eol-value"></span>
|
||||||
</span>
|
</span>
|
||||||
<div class="cm-status-bar-select-content no-select">
|
<div class="cm-status-bar-select-content no-select" tabindex="0">
|
||||||
<a href="#" draggable="false" data-val="LF">Line Feed, U+000A</a>
|
<a href="#" draggable="false" data-val="LF" tabindex="0">Line Feed, U+000A</a>
|
||||||
<a href="#" draggable="false" data-val="VT">Vertical Tab, U+000B</a>
|
<a href="#" draggable="false" data-val="VT" tabindex="0">Vertical Tab, U+000B</a>
|
||||||
<a href="#" draggable="false" data-val="FF">Form Feed, U+000C</a>
|
<a href="#" draggable="false" data-val="FF" tabindex="0">Form Feed, U+000C</a>
|
||||||
<a href="#" draggable="false" data-val="CR">Carriage Return, U+000D</a>
|
<a href="#" draggable="false" data-val="CR" tabindex="0">Carriage Return, U+000D</a>
|
||||||
<a href="#" draggable="false" data-val="CRLF">CR+LF, U+000D U+000A</a>
|
<a href="#" draggable="false" data-val="CRLF" tabindex="0">CR+LF, U+000D U+000A</a>
|
||||||
<!-- <a href="#" draggable="false" data-val="NL">Next Line, U+0085</a> This causes problems. -->
|
<!-- <a href="#" draggable="false" data-val="NL" tabindex="0">Next Line, U+0085</a> This causes problems. -->
|
||||||
<a href="#" draggable="false" data-val="LS">Line Separator, U+2028</a>
|
<a href="#" draggable="false" data-val="LS" tabindex="0">Line Separator, U+2028</a>
|
||||||
<a href="#" draggable="false" data-val="PS">Paragraph Separator, U+2029</a>
|
<a href="#" draggable="false" data-val="PS" tabindex="0">Paragraph Separator, U+2029</a>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
@ -476,7 +442,7 @@ function hideOnClickOutside(element, instantiatingEvent) {
|
||||||
* Closes element if click is outside it.
|
* Closes element if click is outside it.
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
const outsideClickListener = (event) => {
|
const outsideClickListener = event => {
|
||||||
// Don't trigger if we're clicking inside the element, or if the element
|
// Don't trigger if we're clicking inside the element, or if the element
|
||||||
// is not visible, or if this is the same click event that opened it.
|
// is not visible, or if this is the same click event that opened it.
|
||||||
if (
|
if (
|
||||||
|
@ -509,14 +475,30 @@ function hideOnMoveFocus(element, instantiatingEvent) {
|
||||||
* Closes element if key press is outside it.
|
* Closes element if key press is outside it.
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
const outsideClickListener = (event) => {
|
const outsidePressListener = (event) => {
|
||||||
// Don't trigger if we're pressing keys while inside the element, or if the element
|
// Don't trigger if we're pressing keys while inside the element, or if the element
|
||||||
// is not visible, or if this is the same click event that opened it.
|
// is not visible, or if this is the same click event that opened it.
|
||||||
if (
|
if (
|
||||||
!element.contains(event.target) &&
|
!element.contains(event.target) &&
|
||||||
|
event.timeStamp !== instantiatingEvent.timeStamp &&
|
||||||
|
event.key !== "ArrowUp"
|
||||||
|
) {
|
||||||
|
hideElement(element);
|
||||||
|
} else if (
|
||||||
|
event.key === "Escape" &&
|
||||||
event.timeStamp !== instantiatingEvent.timeStamp
|
event.timeStamp !== instantiatingEvent.timeStamp
|
||||||
) {
|
) {
|
||||||
hideElement(element);
|
hideElement(element);
|
||||||
|
} else if (
|
||||||
|
event.key === "ArrowUp" ||
|
||||||
|
(event.key === "ArrowDown" &&
|
||||||
|
event.timeStamp !== instantiatingEvent.timeStamp)
|
||||||
|
) {
|
||||||
|
const menuItems = element.getElementsByTagName("a");
|
||||||
|
menuItems[0].focus();
|
||||||
|
|
||||||
|
console.log("ev target:", event.target);
|
||||||
|
console.log("element", element);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -526,7 +508,7 @@ function hideOnMoveFocus(element, instantiatingEvent) {
|
||||||
element
|
element
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
elementsWithKeyDownListeners[element] = outsideClickListener;
|
elementsWithKeyDownListeners[element] = outsidePressListener;
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"keydown",
|
"keydown",
|
||||||
elementsWithKeyDownListeners[element],
|
elementsWithKeyDownListeners[element],
|
||||||
|
@ -535,6 +517,38 @@ function hideOnMoveFocus(element, instantiatingEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for menu item keydown events
|
||||||
|
* Moves focus to next/previous element based on arrow direction.
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
const arrowNav = (event) => {
|
||||||
|
const currentElement = event.target;
|
||||||
|
if (event.key === "ArrowDown") {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
const nextElement = currentElement.nextElementSibling;
|
||||||
|
if (nextElement === null) {
|
||||||
|
currentElement.parentElement.firstElementChild.focus();
|
||||||
|
} else {
|
||||||
|
nextElement.focus();
|
||||||
|
}
|
||||||
|
} else if (event.key === "ArrowUp") {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
const prevElement = currentElement.previousElementSibling;
|
||||||
|
if (prevElement === null) {
|
||||||
|
currentElement.parentElement.lastElementChild.focus();
|
||||||
|
} else {
|
||||||
|
prevElement.focus();
|
||||||
|
}
|
||||||
|
} else if (event.key === "Tab") {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
currentElement.parentElement.closest(".cm-status-bar-select-content").previousElementSibling.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the specified element and removes the click or keydown listener for it
|
* Hides the specified element and removes the click or keydown listener for it
|
||||||
* @param {Element} element
|
* @param {Element} element
|
||||||
|
|
|
@ -152,9 +152,7 @@ class ControlsWaiter {
|
||||||
|
|
||||||
const params = [
|
const params = [
|
||||||
includeRecipe ? ["recipe", recipeStr] : undefined,
|
includeRecipe ? ["recipe", recipeStr] : undefined,
|
||||||
includeInput && input.length
|
includeInput && input.length ? ["input", Utils.escapeHtml(input)] : undefined,
|
||||||
? ["input", Utils.escapeHtml(input)]
|
|
||||||
: undefined,
|
|
||||||
inputChrEnc !== 0 ? ["ienc", inputChrEnc] : undefined,
|
inputChrEnc !== 0 ? ["ienc", inputChrEnc] : undefined,
|
||||||
outputChrEnc !== 0 ? ["oenc", outputChrEnc] : undefined,
|
outputChrEnc !== 0 ? ["oenc", outputChrEnc] : undefined,
|
||||||
inputEOLSeq !== "\n" ? ["ieol", inputEOLSeq] : undefined,
|
inputEOLSeq !== "\n" ? ["ieol", inputEOLSeq] : undefined,
|
||||||
|
@ -255,9 +253,7 @@ class ControlsWaiter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedRecipes = localStorage.savedRecipes
|
const savedRecipes = localStorage.savedRecipes ? JSON.parse(localStorage.savedRecipes) : [];
|
||||||
? JSON.parse(localStorage.savedRecipes)
|
|
||||||
: [];
|
|
||||||
let recipeId = localStorage.recipeId || 0;
|
let recipeId = localStorage.recipeId || 0;
|
||||||
|
|
||||||
savedRecipes.push({
|
savedRecipes.push({
|
||||||
|
@ -287,9 +283,7 @@ class ControlsWaiter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add recipes to select
|
// Add recipes to select
|
||||||
const savedRecipes = localStorage.savedRecipes
|
const savedRecipes = localStorage.savedRecipes ? JSON.parse(localStorage.savedRecipes) : [];
|
||||||
? JSON.parse(localStorage.savedRecipes)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
for (i = 0; i < savedRecipes.length; i++) {
|
for (i = 0; i < savedRecipes.length; i++) {
|
||||||
const opt = document.createElement("option");
|
const opt = document.createElement("option");
|
||||||
|
@ -316,9 +310,7 @@ class ControlsWaiter {
|
||||||
if (!this.app.isLocalStorageAvailable()) return false;
|
if (!this.app.isLocalStorageAvailable()) return false;
|
||||||
|
|
||||||
const id = parseInt(document.getElementById("load-name").value, 10);
|
const id = parseInt(document.getElementById("load-name").value, 10);
|
||||||
const rawSavedRecipes = localStorage.savedRecipes
|
const rawSavedRecipes = localStorage.savedRecipes ? JSON.parse(localStorage.savedRecipes) : [];
|
||||||
? JSON.parse(localStorage.savedRecipes)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const savedRecipes = rawSavedRecipes.filter((r) => r.id !== id);
|
const savedRecipes = rawSavedRecipes.filter((r) => r.id !== id);
|
||||||
|
|
||||||
|
@ -333,9 +325,7 @@ class ControlsWaiter {
|
||||||
if (!this.app.isLocalStorageAvailable()) return false;
|
if (!this.app.isLocalStorageAvailable()) return false;
|
||||||
|
|
||||||
const el = e.target;
|
const el = e.target;
|
||||||
const savedRecipes = localStorage.savedRecipes
|
const savedRecipes = localStorage.savedRecipes ? JSON.parse(localStorage.savedRecipes) : [];
|
||||||
? JSON.parse(localStorage.savedRecipes)
|
|
||||||
: [];
|
|
||||||
const id = parseInt(el.value, 10);
|
const id = parseInt(el.value, 10);
|
||||||
|
|
||||||
const recipe = savedRecipes.find((r) => r.id === id);
|
const recipe = savedRecipes.find((r) => r.id === id);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue