formatting

This commit is contained in:
e218736 2024-02-21 16:13:56 +00:00
parent 6fcf103760
commit 4c31c16eef
2 changed files with 75 additions and 94 deletions

View file

@ -82,24 +82,24 @@ class StatusBarPanel {
* @param {Event} e * @param {Event} e
*/ */
showDropUp(e) { showDropUp(e) {
if (e.type === "click" || e.keyCode === 13) { if (e.type === "click" || e.key === 'Enter'|| e.key === ' ') {
const el = e.target const el = e.target
.closest(".cm-status-bar-select") .closest(".cm-status-bar-select")
.querySelector(".cm-status-bar-select-content"); .querySelector(".cm-status-bar-select-content");
const btn = e.target.closest(".cm-status-bar-select-btn"); const btn = e.target.closest(".cm-status-bar-select-btn");
if (btn.classList.contains("disabled")) return; if (btn.classList.contains("disabled")) return;
el.classList.add("show"); el.classList.add("show");
// Focus the filter input if present // Focus the filter input if present
const filter = el.querySelector(".cm-status-bar-filter-input"); const filter = el.querySelector(".cm-status-bar-filter-input");
if (filter) filter.focus(); if (filter) filter.focus();
// Set up a listener to close the menu if the user clicks outside of it // Set up a listener to close the menu if the user clicks outside of it
hideOnClickOutside(el, e); hideOnClickOutside(el, e);
// Set up a listener to close the menu if the user presses key outside of it // Set up a listener to close the menu if the user presses key outside of it
hideOnMoveFocus(el, e); hideOnMoveFocus(el, e);
} }
} }
@ -181,8 +181,7 @@ class StatusBarPanel {
// CodeMirror always counts line breaks as one character. // CodeMirror always counts line breaks as one character.
// We want to show an accurate reading of how many bytes there are. // We want to show an accurate reading of how many bytes there are.
if (state.lineBreak.length !== 1) { if (state.lineBreak.length !== 1) {
docLength += docLength += (state.lineBreak.length * state.doc.lines) - state.doc.lines - 1;
state.lineBreak.length * state.doc.lines - state.doc.lines - 1;
} }
length.textContent = docLength; length.textContent = docLength;
lines.textContent = state.doc.lines; lines.textContent = state.doc.lines;
@ -237,6 +236,7 @@ class StatusBarPanel {
} }
} }
/** /**
* Sets the current EOL separator in the status bar * Sets the current EOL separator in the status bar
* @param {EditorState} state * @param {EditorState} state
@ -344,7 +344,7 @@ class StatusBarPanel {
*/ */
constructLHS() { constructLHS() {
return ` return `
<span data-toggle="tooltip" tabindex="0" title="${this.label} length" data-help-title="${this.label} length" data-help="This number represents the number of characters in the ${this.label}.<br><br>The CRLF end of line separator is counted as two characters which impacts this value."> <span data-toggle="tooltip" tabindex="0" title="${this.label} length" data-help-title="${this.label} length" data-help="This number represents the number of characters in the ${this.label}.<br><br>The CRLF end of line separator is counted as two characters which impacts this value.">
<i class="material-icons">abc</i> <i class="material-icons">abc</i>
<span class="stats-length-value"></span> <span class="stats-length-value"></span>
</span> </span>
@ -386,13 +386,13 @@ class StatusBarPanel {
} }
return ` return `
<span class="baking-time-info" style="display: none" data-toggle="tooltip" tabindex="0" data-html="true" title="Baking time" data-help-title="Baking time" data-help="The baking time is the total time between data being read from the input, processed, and then displayed in the output.<br><br>The 'Threading overhead' value accounts for the transfer of data between different processing threads, as well as some garbage collection. It is not included in the overall bake time displayed in the status bar as it is largely influenced by background operating system and browser activity which can fluctuate significantly."> <span class="baking-time-info" style="display: none" data-toggle="tooltip" tabindex="0" data-html="true" title="Baking time" data-help-title="Baking time" data-help="The baking time is the total time between data being read from the input, processed, and then displayed in the output.<br><br>The 'Threading overhead' value accounts for the transfer of data between different processing threads, as well as some garbage collection. It is not included in the overall bake time displayed in the status bar as it is largely influenced by background operating system and browser activity which can fluctuate significantly.">
<i class="material-icons">schedule</i> <i class="material-icons">schedule</i>
<span class="baking-time-value"></span>ms <span class="baking-time-value"></span>ms
</span> </span>
<div class="cm-status-bar-select chr-enc-select" data-help-title="${this.label} character encoding" data-help="${chrEncHelpText}"> <div class="cm-status-bar-select chr-enc-select" data-help-title="${this.label} character encoding" data-help="${chrEncHelpText}">
<span class="cm-status-bar-select-btn" data-toggle="tooltip" tabindex="0" data-html="true" data-placement="left" title="${this.label} character encoding"> <span class="cm-status-bar-select-btn" data-toggle="tooltip" tabindex="0" data-html="true" data-placement="left" title="${this.label} character encoding">
<i class="material-icons">text_fields</i> <span class="chr-enc-value">Raw Bytes</span> <i class="material-icons">text_fields</i> <span class="chr-enc-value">Raw Bytes</span>
</span> </span>
<div class="cm-status-bar-select-content"> <div class="cm-status-bar-select-content">
@ -427,6 +427,7 @@ class StatusBarPanel {
</div> </div>
</div>`; </div>`;
} }
} }
const elementsWithListeners = {}; const elementsWithListeners = {};
@ -447,19 +448,14 @@ function hideOnClickOutside(element, instantiatingEvent) {
// 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.timeStamp !== instantiatingEvent.timeStamp) {
) {
hideElement(element); hideElement(element);
} }
}; };
if (!Object.prototype.hasOwnProperty.call(elementsWithListeners, element)) { if (!Object.prototype.hasOwnProperty.call(elementsWithListeners, element)) {
elementsWithListeners[element] = outsideClickListener; elementsWithListeners[element] = outsideClickListener;
document.addEventListener( document.addEventListener("click", elementsWithListeners[element], false);
"click",
elementsWithListeners[element],
false
);
} }
} }
@ -555,20 +551,13 @@ const arrowNav = (event) => {
*/ */
function hideElement(element) { function hideElement(element) {
element.classList.remove("show"); element.classList.remove("show");
document.removeEventListener( document.removeEventListener("click", elementsWithListeners[element], false);
"click", document.removeEventListener("keydown", elementsWithKeyDownListeners[element], false);
elementsWithListeners[element],
false
);
document.removeEventListener(
"keydown",
elementsWithKeyDownListeners[element],
false
);
delete elementsWithListeners[element]; delete elementsWithListeners[element];
delete elementsWithKeyDownListeners[element]; delete elementsWithKeyDownListeners[element];
} }
/** /**
* A panel constructor factory building a panel that re-counts the stats every time the document changes. * A panel constructor factory building a panel that re-counts the stats every time the document changes.
* @param {Object} opts * @param {Object} opts
@ -586,7 +575,7 @@ function makePanel(opts) {
sbPanel.monitorHTMLOutput(); sbPanel.monitorHTMLOutput();
return { return {
dom: sbPanel.dom, "dom": sbPanel.dom,
update(update) { update(update) {
sbPanel.updateEOL(update.state); sbPanel.updateEOL(update.state);
sbPanel.updateCharEnc(); sbPanel.updateCharEnc();
@ -599,7 +588,7 @@ function makePanel(opts) {
if (update.docChanged) { if (update.docChanged) {
sbPanel.updateStats(update.state); sbPanel.updateStats(update.state);
} }
}, }
}; };
}; };
} }

View file

@ -6,10 +6,12 @@
import Utils from "../../core/Utils.mjs"; import Utils from "../../core/Utils.mjs";
/** /**
* Waiter to handle events related to the CyberChef controls (i.e. Bake, Step, Save, Load etc.) * Waiter to handle events related to the CyberChef controls (i.e. Bake, Step, Save, Load etc.)
*/ */
class ControlsWaiter { class ControlsWaiter {
/** /**
* ControlsWaiter constructor. * ControlsWaiter constructor.
* *
@ -21,6 +23,7 @@ class ControlsWaiter {
this.manager = manager; this.manager = manager;
} }
/** /**
* Initialise Bootstrap components * Initialise Bootstrap components
*/ */
@ -30,10 +33,11 @@ class ControlsWaiter {
animation: false, animation: false,
container: "body", container: "body",
boundary: "viewport", boundary: "viewport",
trigger: "hover focus", trigger: "hover focus"
}); });
} }
/** /**
* Checks or unchecks the Auto Bake checkbox based on the given value. * Checks or unchecks the Auto Bake checkbox based on the given value.
* *
@ -47,6 +51,7 @@ class ControlsWaiter {
} }
} }
/** /**
* Handler to trigger baking. * Handler to trigger baking.
*/ */
@ -59,6 +64,7 @@ class ControlsWaiter {
} }
} }
/** /**
* Handler for the 'Step through' command. Executes the next step of the recipe. * Handler for the 'Step through' command. Executes the next step of the recipe.
*/ */
@ -66,6 +72,7 @@ class ControlsWaiter {
this.app.step(); this.app.step();
} }
/** /**
* Handler for changes made to the Auto Bake checkbox. * Handler for changes made to the Auto Bake checkbox.
*/ */
@ -73,6 +80,7 @@ class ControlsWaiter {
this.app.autoBake_ = document.getElementById("auto-bake").checked; this.app.autoBake_ = document.getElementById("auto-bake").checked;
} }
/** /**
* Handler for the 'Clear recipe' command. Removes all operations from the recipe. * Handler for the 'Clear recipe' command. Removes all operations from the recipe.
*/ */
@ -80,6 +88,7 @@ class ControlsWaiter {
this.manager.recipe.clearRecipe(); this.manager.recipe.clearRecipe();
} }
/** /**
* Populates the save dialog box with a URL incorporating the recipe and input. * Populates the save dialog box with a URL incorporating the recipe and input.
* *
@ -88,23 +97,16 @@ class ControlsWaiter {
initialiseSaveLink(recipeConfig) { initialiseSaveLink(recipeConfig) {
recipeConfig = recipeConfig || this.app.getRecipeConfig(); recipeConfig = recipeConfig || this.app.getRecipeConfig();
const includeRecipe = document.getElementById( const includeRecipe = document.getElementById("save-link-recipe-checkbox").checked;
"save-link-recipe-checkbox" const includeInput = document.getElementById("save-link-input-checkbox").checked;
).checked;
const includeInput = document.getElementById("save-link-input-checkbox")
.checked;
const saveLinkEl = document.getElementById("save-link"); const saveLinkEl = document.getElementById("save-link");
const saveLink = this.generateStateUrl( const saveLink = this.generateStateUrl(includeRecipe, includeInput, null, recipeConfig);
includeRecipe,
includeInput,
null,
recipeConfig
);
saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120)); saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120));
saveLinkEl.setAttribute("href", saveLink); saveLinkEl.setAttribute("href", saveLink);
} }
/** /**
* Generates a URL containing the current recipe and input state. * Generates a URL containing the current recipe and input state.
* *
@ -115,24 +117,15 @@ class ControlsWaiter {
* @param {string} [baseURL] - The CyberChef URL, set to the current URL if not included * @param {string} [baseURL] - The CyberChef URL, set to the current URL if not included
* @returns {string} * @returns {string}
*/ */
generateStateUrl( generateStateUrl(includeRecipe, includeInput, input, recipeConfig, baseURL) {
includeRecipe,
includeInput,
input,
recipeConfig,
baseURL
) {
recipeConfig = recipeConfig || this.app.getRecipeConfig(); recipeConfig = recipeConfig || this.app.getRecipeConfig();
const link = const link = baseURL || window.location.protocol + "//" +
baseURL ||
window.location.protocol +
"//" +
window.location.host + window.location.host +
window.location.pathname; window.location.pathname;
const recipeStr = Utils.generatePrettyRecipe(recipeConfig); const recipeStr = Utils.generatePrettyRecipe(recipeConfig);
includeRecipe = includeRecipe && recipeConfig.length > 0; includeRecipe = includeRecipe && (recipeConfig.length > 0);
// If we don't get passed an input, get it from the current URI // If we don't get passed an input, get it from the current URI
if (input === null && includeInput) { if (input === null && includeInput) {
@ -156,11 +149,11 @@ class ControlsWaiter {
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,
outputEOLSeq !== "\n" ? ["oeol", outputEOLSeq] : undefined, outputEOLSeq !== "\n" ? ["oeol", outputEOLSeq] : undefined
]; ];
const hash = params const hash = params
.filter((v) => v) .filter(v => v)
.map(([key, value]) => `${key}=${Utils.encodeURIFragment(value)}`) .map(([key, value]) => `${key}=${Utils.encodeURIFragment(value)}`)
.join("&"); .join("&");
@ -171,6 +164,7 @@ class ControlsWaiter {
return link; return link;
} }
/** /**
* Handler for changes made to the save dialog text area. Re-initialises the save link. * Handler for changes made to the save dialog text area. Re-initialises the save link.
*/ */
@ -181,6 +175,7 @@ class ControlsWaiter {
} catch (err) {} } catch (err) {}
} }
/** /**
* Handler for the 'Save' command. Pops up the save dialog box. * Handler for the 'Save' command. Pops up the save dialog box.
*/ */
@ -188,14 +183,8 @@ class ControlsWaiter {
const recipeConfig = this.app.getRecipeConfig(); const recipeConfig = this.app.getRecipeConfig();
const recipeStr = JSON.stringify(recipeConfig); const recipeStr = JSON.stringify(recipeConfig);
document.getElementById( document.getElementById("save-text-chef").value = Utils.generatePrettyRecipe(recipeConfig, true);
"save-text-chef" document.getElementById("save-text-clean").value = JSON.stringify(recipeConfig, null, 2)
).value = Utils.generatePrettyRecipe(recipeConfig, true);
document.getElementById("save-text-clean").value = JSON.stringify(
recipeConfig,
null,
2
)
.replace(/{\n\s+"/g, '{ "') .replace(/{\n\s+"/g, '{ "')
.replace(/\[\n\s{3,}/g, "[") .replace(/\[\n\s{3,}/g, "[")
.replace(/\n\s{3,}]/g, "]") .replace(/\n\s{3,}]/g, "]")
@ -207,6 +196,7 @@ class ControlsWaiter {
$("#save-modal").modal(); $("#save-modal").modal();
} }
/** /**
* Handler for the save link recipe checkbox change event. * Handler for the save link recipe checkbox change event.
*/ */
@ -214,6 +204,7 @@ class ControlsWaiter {
this.initialiseSaveLink(); this.initialiseSaveLink();
} }
/** /**
* Handler for the save link input checkbox change event. * Handler for the save link input checkbox change event.
*/ */
@ -221,6 +212,7 @@ class ControlsWaiter {
this.initialiseSaveLink(); this.initialiseSaveLink();
} }
/** /**
* Handler for the 'Load' command. Pops up the load dialog box. * Handler for the 'Load' command. Pops up the load dialog box.
*/ */
@ -229,6 +221,7 @@ class ControlsWaiter {
$("#load-modal").modal(); $("#load-modal").modal();
} }
/** /**
* Saves the recipe specified in the save textarea to local storage. * Saves the recipe specified in the save textarea to local storage.
*/ */
@ -241,25 +234,22 @@ class ControlsWaiter {
return false; return false;
} }
const recipeName = Utils.escapeHtml( const recipeName = Utils.escapeHtml(document.getElementById("save-name").value);
document.getElementById("save-name").value const recipeStr = document.querySelector("#save-texts .tab-pane.active textarea").value;
);
const recipeStr = document.querySelector(
"#save-texts .tab-pane.active textarea"
).value;
if (!recipeName) { if (!recipeName) {
this.app.alert("Please enter a recipe name", 3000); this.app.alert("Please enter a recipe name", 3000);
return; return;
} }
const savedRecipes = localStorage.savedRecipes ? JSON.parse(localStorage.savedRecipes) : []; const savedRecipes = localStorage.savedRecipes ?
JSON.parse(localStorage.savedRecipes) : [];
let recipeId = localStorage.recipeId || 0; let recipeId = localStorage.recipeId || 0;
savedRecipes.push({ savedRecipes.push({
id: ++recipeId, id: ++recipeId,
name: recipeName, name: recipeName,
recipe: recipeStr, recipe: recipeStr
}); });
localStorage.savedRecipes = JSON.stringify(savedRecipes); localStorage.savedRecipes = JSON.stringify(savedRecipes);
@ -268,6 +258,7 @@ class ControlsWaiter {
this.app.alert(`Recipe saved as "${recipeName}".`, 3000); this.app.alert(`Recipe saved as "${recipeName}".`, 3000);
} }
/** /**
* Populates the list of saved recipes in the load dialog box from local storage. * Populates the list of saved recipes in the load dialog box from local storage.
*/ */
@ -283,15 +274,14 @@ class ControlsWaiter {
} }
// Add recipes to select // Add recipes to select
const savedRecipes = localStorage.savedRecipes ? JSON.parse(localStorage.savedRecipes) : []; const savedRecipes = 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");
opt.value = savedRecipes[i].id; opt.value = savedRecipes[i].id;
// Unescape then re-escape in case localStorage has been corrupted // Unescape then re-escape in case localStorage has been corrupted
opt.innerHTML = Utils.escapeHtml( opt.innerHTML = Utils.escapeHtml(Utils.unescapeHtml(savedRecipes[i].name));
Utils.unescapeHtml(savedRecipes[i].name)
);
loadNameEl.appendChild(opt); loadNameEl.appendChild(opt);
} }
@ -303,6 +293,7 @@ class ControlsWaiter {
loadText.dispatchEvent(evt); loadText.dispatchEvent(evt);
} }
/** /**
* Removes the currently selected recipe from local storage. * Removes the currently selected recipe from local storage.
*/ */
@ -310,14 +301,16 @@ 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 ? JSON.parse(localStorage.savedRecipes) : []; const rawSavedRecipes = localStorage.savedRecipes ?
JSON.parse(localStorage.savedRecipes) : [];
const savedRecipes = rawSavedRecipes.filter((r) => r.id !== id); const savedRecipes = rawSavedRecipes.filter(r => r.id !== id);
localStorage.savedRecipes = JSON.stringify(savedRecipes); localStorage.savedRecipes = JSON.stringify(savedRecipes);
this.populateLoadRecipesList(); this.populateLoadRecipesList();
} }
/** /**
* Displays the selected recipe in the load text box. * Displays the selected recipe in the load text box.
*/ */
@ -325,22 +318,22 @@ 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 ? JSON.parse(localStorage.savedRecipes) : []; const savedRecipes = 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);
document.getElementById("load-text").value = recipe.recipe; document.getElementById("load-text").value = recipe.recipe;
} }
/** /**
* Loads the selected recipe and populates the Recipe with its operations. * Loads the selected recipe and populates the Recipe with its operations.
*/ */
loadButtonClick() { loadButtonClick() {
try { try {
const recipeConfig = Utils.parseRecipeConfig( const recipeConfig = Utils.parseRecipeConfig(document.getElementById("load-text").value);
document.getElementById("load-text").value
);
this.app.setRecipeConfig(recipeConfig); this.app.setRecipeConfig(recipeConfig);
this.app.autoBake(); this.app.autoBake();
@ -350,6 +343,7 @@ class ControlsWaiter {
} }
} }
/** /**
* Populates the bug report information box with useful technical info. * Populates the bug report information box with useful technical info.
* *
@ -359,13 +353,7 @@ class ControlsWaiter {
e.preventDefault(); e.preventDefault();
const reportBugInfo = document.getElementById("report-bug-info"); const reportBugInfo = document.getElementById("report-bug-info");
const saveLink = this.generateStateUrl( const saveLink = this.generateStateUrl(true, true, null, null, "https://gchq.github.io/CyberChef/");
true,
true,
null,
null,
"https://gchq.github.io/CyberChef/"
);
if (reportBugInfo) { if (reportBugInfo) {
reportBugInfo.innerHTML = `* Version: ${PKG_VERSION} reportBugInfo.innerHTML = `* Version: ${PKG_VERSION}
@ -378,6 +366,7 @@ ${navigator.userAgent}
} }
} }
/** /**
* Shows the stale indicator to show that the input or recipe has changed * Shows the stale indicator to show that the input or recipe has changed
* since the last bake. * since the last bake.
@ -387,6 +376,7 @@ ${navigator.userAgent}
staleIndicator.classList.remove("hidden"); staleIndicator.classList.remove("hidden");
} }
/** /**
* Hides the stale indicator to show that the input or recipe has not changed * Hides the stale indicator to show that the input or recipe has not changed
* since the last bake. * since the last bake.
@ -396,6 +386,7 @@ ${navigator.userAgent}
staleIndicator.classList.add("hidden"); staleIndicator.classList.add("hidden");
} }
/** /**
* Switches the Bake button between 'Bake', 'Cancel' and 'Loading' functions. * Switches the Bake button between 'Bake', 'Cancel' and 'Loading' functions.
* *
@ -438,6 +429,7 @@ ${navigator.userAgent}
recList.style.bottom = controls.clientHeight + "px"; recList.style.bottom = controls.clientHeight + "px";
} }
} }
export default ControlsWaiter; export default ControlsWaiter;