mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-20 23:06:16 -04:00
Add UI for input tabs.
Can add, remove and switch tabs
This commit is contained in:
parent
328c0ade22
commit
37218c1e81
5 changed files with 304 additions and 34 deletions
|
@ -349,6 +349,189 @@ class InputWaiter {
|
||||||
window.dispatchEvent(this.manager.statechange);
|
window.dispatchEvent(this.manager.statechange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for adding a new input tab.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
addTab() {
|
||||||
|
const tabWrapper = document.getElementById("input-tabs");
|
||||||
|
const tabsList = tabWrapper.children[0];
|
||||||
|
const lastTabNum = tabsList.lastElementChild.id.replace("input-tab-", "");
|
||||||
|
const newTabNum = parseInt(lastTabNum, 10) + 1;
|
||||||
|
|
||||||
|
tabWrapper.style.display = "block";
|
||||||
|
|
||||||
|
// Resize highlighter
|
||||||
|
// document.getElementById("input-highlighter").style.height = "calc(100% - var(--tab-height) - var(--title-height))";
|
||||||
|
|
||||||
|
const activeTabElements = document.getElementsByClassName("active-input-tab");
|
||||||
|
for (let i = 0; i < activeTabElements.length; i++) {
|
||||||
|
activeTabElements.item(i).classList.remove("active-input-tab");
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTab = document.createElement("li");
|
||||||
|
newTab.id = `input-tab-${newTabNum}`;
|
||||||
|
newTab.classList.add("active-input-tab");
|
||||||
|
|
||||||
|
const newTabContent = document.createElement("div");
|
||||||
|
newTabContent.classList.add("input-tab-content");
|
||||||
|
newTabContent.innerText = `Tab ${newTabNum}`;
|
||||||
|
|
||||||
|
const newTabCloseBtn = document.createElement("button");
|
||||||
|
newTabCloseBtn.className = "btn btn-primary bmd-btn-icon btn-close-tab";
|
||||||
|
newTabCloseBtn.id = `btn-close-tab-${newTabNum}`;
|
||||||
|
|
||||||
|
const newTabCloseBtnIcon = document.createElement("i");
|
||||||
|
newTabCloseBtnIcon.classList.add("material-icons");
|
||||||
|
newTabCloseBtnIcon.innerText = "clear";
|
||||||
|
|
||||||
|
newTabCloseBtn.appendChild(newTabCloseBtnIcon);
|
||||||
|
newTab.appendChild(newTabContent);
|
||||||
|
newTab.appendChild(newTabCloseBtn);
|
||||||
|
|
||||||
|
tabsList.appendChild(newTab);
|
||||||
|
|
||||||
|
const multiWrapper = document.getElementById("multi-input-wrapper");
|
||||||
|
|
||||||
|
const activeAreaElements = document.getElementsByClassName("active-input-area");
|
||||||
|
for (let i = 0; i < activeAreaElements.length; i++) {
|
||||||
|
activeAreaElements.item(i).classList.remove("active-input-area");
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTextAreaWrapper = document.createElement("div");
|
||||||
|
newTextAreaWrapper.className = "textarea-wrapper no-select input-wrapper active-input-area";
|
||||||
|
newTextAreaWrapper.id = `tab-input-area-${newTabNum}`;
|
||||||
|
|
||||||
|
const newTextArea = document.createElement("textarea");
|
||||||
|
newTextArea.id = `input-text-${newTabNum}`;
|
||||||
|
newTextArea.spellcheck = "false";
|
||||||
|
newTextArea.classList.add("input-text");
|
||||||
|
|
||||||
|
const newFileArea = document.createElement("div");
|
||||||
|
newFileArea.id = `input-file-${newTabNum}`;
|
||||||
|
newFileArea.classList.add("input-file");
|
||||||
|
|
||||||
|
const newFileOverlay = document.createElement("div");
|
||||||
|
newFileOverlay.classList.add("file-overlay");
|
||||||
|
|
||||||
|
const newFileWrapper = document.createElement("div");
|
||||||
|
newFileWrapper.style.position = "relative";
|
||||||
|
newFileWrapper.style.height = "100%";
|
||||||
|
|
||||||
|
const newFileCard = document.createElement("div");
|
||||||
|
newFileCard.className = "io-card card";
|
||||||
|
|
||||||
|
const newFileThumb = document.createElement("img");
|
||||||
|
newFileThumb["aria-hidden"] = "true";
|
||||||
|
newFileThumb.src = require("./static/images/file-128x128.png");
|
||||||
|
newFileThumb.alt = "File icon";
|
||||||
|
newFileThumb.id = `input-file-thumbnail-${newTabNum}`;
|
||||||
|
|
||||||
|
const newFileCardBody = document.createElement("div");
|
||||||
|
newFileCardBody.class = "card-body";
|
||||||
|
|
||||||
|
const newFileCloseButton = document.createElement("button");
|
||||||
|
newFileCloseButton.type = "button";
|
||||||
|
newFileCloseButton.class = "close";
|
||||||
|
newFileCloseButton.id = `input-file-close-${newTabNum}`;
|
||||||
|
newFileCloseButton.innerHTML = "×";
|
||||||
|
|
||||||
|
newFileCardBody.appendChild(newFileCloseButton);
|
||||||
|
|
||||||
|
const cardInfo = `
|
||||||
|
Name: <span id="input-file-name-${newTabNum}"></span><br>
|
||||||
|
Size: <span id="input-file-size-${newTabNum}"></span><br>
|
||||||
|
Type: <span id="input-file-type-${newTabNum}"></span><br>
|
||||||
|
Loaded: <span id="input-file-loaded-${newTabNum}"></span>`;
|
||||||
|
|
||||||
|
newFileCardBody.innerHTML = newFileCardBody.innerHTML + cardInfo;
|
||||||
|
|
||||||
|
newFileCard.appendChild(newFileThumb);
|
||||||
|
newFileCard.appendChild(newFileCardBody);
|
||||||
|
newFileWrapper.appendChild(newFileCard);
|
||||||
|
newFileArea.appendChild(newFileOverlay);
|
||||||
|
newFileArea.appendChild(newFileWrapper);
|
||||||
|
|
||||||
|
newTextAreaWrapper.appendChild(newTextArea);
|
||||||
|
newTextAreaWrapper.appendChild(newFileArea);
|
||||||
|
multiWrapper.appendChild(newTextAreaWrapper);
|
||||||
|
|
||||||
|
// file inputs!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for removing an input tab
|
||||||
|
*
|
||||||
|
* @param {event} mouseEvent
|
||||||
|
*/
|
||||||
|
removeTab(mouseEvent) {
|
||||||
|
if (!mouseEvent.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const closeBtn = mouseEvent.path[1];
|
||||||
|
const liItem = closeBtn.parentElement;
|
||||||
|
const tabList = liItem.parentElement;
|
||||||
|
if (tabList.children.length > 1) {
|
||||||
|
if (liItem.classList.contains("active-input-tab")) {
|
||||||
|
// If current tab is active, change the active tab and input to another tab
|
||||||
|
let newActiveAreaId;
|
||||||
|
if (liItem.previousElementSibling) {
|
||||||
|
liItem.previousElementSibling.classList.add("active-input-tab");
|
||||||
|
const newActiveTabNum = liItem.previousElementSibling.id.replace("input-tab-", "");
|
||||||
|
newActiveAreaId = `tab-input-area-${newActiveTabNum}`;
|
||||||
|
} else if (liItem.nextElementSibling) {
|
||||||
|
liItem.nextElementSibling.classList.add("active-input-tab");
|
||||||
|
const newActiveTabNum = liItem.nextElementSibling.id.replace("input-tab-", "");
|
||||||
|
newActiveAreaId = `tab-input-area-${newActiveTabNum}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newActiveAreaId) {
|
||||||
|
document.getElementById(newActiveAreaId).classList.add("active-input-area");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tabNum = liItem.id.replace("input-tab-", "");
|
||||||
|
const multiInputArea = document.getElementById("multi-input-wrapper");
|
||||||
|
const inputAreaId = `tab-input-area-${tabNum}`;
|
||||||
|
const inputArea = document.getElementById(inputAreaId);
|
||||||
|
|
||||||
|
tabList.removeChild(liItem);
|
||||||
|
multiInputArea.removeChild(inputArea);
|
||||||
|
}
|
||||||
|
if (tabList.children.length === 1) {
|
||||||
|
document.getElementById("input-tabs").style.display = "none";
|
||||||
|
// document.getElementById("input-highlighter").style.height = "calc(100% - var(--title-height))";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for changing tabs
|
||||||
|
*
|
||||||
|
* @param {event} mouseEvent
|
||||||
|
*/
|
||||||
|
changeTab(mouseEvent) {
|
||||||
|
if (!mouseEvent.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tabContent = mouseEvent.path[0];
|
||||||
|
const liItem = tabContent.parentElement;
|
||||||
|
const tabNum = liItem.id.replace("input-tab-", "");
|
||||||
|
|
||||||
|
const activeTabsList = document.getElementsByClassName("active-input-tab");
|
||||||
|
for (let i = 0; i < activeTabsList.length; i++) {
|
||||||
|
activeTabsList.item(i).classList.remove("active-input-tab");
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeAreaList = document.getElementsByClassName("active-input-area");
|
||||||
|
for (let i = 0; i < activeAreaList.length; i++) {
|
||||||
|
activeAreaList.item(i).classList.remove("active-input-area");
|
||||||
|
}
|
||||||
|
liItem.classList.add("active-input-tab");
|
||||||
|
|
||||||
|
const newActiveAreaId = `tab-input-area-${tabNum}`;
|
||||||
|
document.getElementById(newActiveAreaId).classList.add("active-input-area");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InputWaiter;
|
export default InputWaiter;
|
||||||
|
|
|
@ -142,19 +142,22 @@ class Manager {
|
||||||
this.addDynamicListener("textarea.arg", "drop", this.recipe.textArgDrop, this.recipe);
|
this.addDynamicListener("textarea.arg", "drop", this.recipe.textArgDrop, this.recipe);
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input);
|
// this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input);
|
||||||
this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input);
|
// this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input);
|
||||||
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
||||||
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||||
this.addListeners("#open-file", "change", this.input.inputOpen, this.input);
|
this.addListeners("#open-file", "change", this.input.inputOpen, this.input);
|
||||||
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
// this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
||||||
this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
|
// this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
|
||||||
this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
|
// this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
|
||||||
document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter));
|
// document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter));
|
||||||
document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter));
|
// document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter));
|
||||||
document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter));
|
// document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter));
|
||||||
this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter);
|
// this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter);
|
||||||
document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
// document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||||
|
document.getElementById("btn-new-tab").addEventListener("click", this.input.addTab);
|
||||||
|
this.addDynamicListener("#input-tabs ul li .btn-close-tab i", "click", this.input.removeTab, this.input);
|
||||||
|
this.addDynamicListener("#input-tabs ul li .input-tab-content", "click", this.input.changeTab, this.input);
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
|
document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
|
||||||
|
|
|
@ -229,6 +229,9 @@
|
||||||
<div class="title no-select">
|
<div class="title no-select">
|
||||||
<label for="input-text">Input</label>
|
<label for="input-text">Input</label>
|
||||||
<span class="float-right">
|
<span class="float-right">
|
||||||
|
<button type="button" class="btn btn-primary bmd-btn-icon" id="btn-new-tab" data-toggle="tooltip" title="Add a new input tab">
|
||||||
|
<i class="material-icons">add</i>
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-primary bmd-btn-icon" id="btn-open-file" data-toggle="tooltip" title="Open file as input" onclick="document.getElementById('open-file').click();">
|
<button type="button" class="btn btn-primary bmd-btn-icon" id="btn-open-file" data-toggle="tooltip" title="Open file as input" onclick="document.getElementById('open-file').click();">
|
||||||
<i class="material-icons">input</i>
|
<i class="material-icons">input</i>
|
||||||
<input type="file" id="open-file" style="display: none">
|
<input type="file" id="open-file" style="display: none">
|
||||||
|
@ -243,20 +246,34 @@
|
||||||
<div class="io-info" id="input-info"></div>
|
<div class="io-info" id="input-info"></div>
|
||||||
<div class="io-info" id="input-selection-info"></div>
|
<div class="io-info" id="input-selection-info"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="textarea-wrapper no-select">
|
<div id="multi-input-wrapper">
|
||||||
|
<div id="input-tabs" style="display: none;">
|
||||||
|
<ul>
|
||||||
|
<li id="input-tab-1" class="active-input-tab">
|
||||||
|
<div class="input-tab-content">
|
||||||
|
Tab 1
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary bmd-btn-icon btn-close-tab" id="btn-close-tab-1">
|
||||||
|
<i class="material-icons">clear</i>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="textarea-wrapper no-select input-wrapper active-input-area" id="tab-input-area-1">
|
||||||
<div id="input-highlighter" class="no-select"></div>
|
<div id="input-highlighter" class="no-select"></div>
|
||||||
<textarea id="input-text" spellcheck="false"></textarea>
|
<textarea id="input-text-1" class="input-text" spellcheck="false"></textarea>
|
||||||
<div id="input-file">
|
<div class="input-file" id="input-file-1">
|
||||||
<div class="file-overlay"></div>
|
<div class="file-overlay"></div>
|
||||||
<div style="position: relative; height: 100%;">
|
<div style="position: relative; height: 100%;">
|
||||||
<div class="io-card card">
|
<div class="io-card card">
|
||||||
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
|
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon" id="input-file-thumbnail-1"/>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<button type="button" class="close" id="input-file-close">×</button>
|
<button type="button" class="close" id="input-file-close">×</button>
|
||||||
Name: <span id="input-file-name"></span><br>
|
Name: <span id="input-file-name-1"></span><br>
|
||||||
Size: <span id="input-file-size"></span><br>
|
Size: <span id="input-file-size-1"></span><br>
|
||||||
Type: <span id="input-file-type"></span><br>
|
Type: <span id="input-file-type-1"></span><br>
|
||||||
Loaded: <span id="input-file-loaded"></span>
|
Loaded: <span id="input-file-loaded-1"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--title-height: 48px;
|
--title-height: 48px;
|
||||||
|
--tab-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#input-text,
|
.input-text,
|
||||||
#output-text,
|
#output-text,
|
||||||
#output-html {
|
#output-html {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -30,12 +30,69 @@
|
||||||
-moz-padding-start: 1px; /* Fixes bug in Firefox */
|
-moz-padding-start: 1px; /* Fixes bug in Firefox */
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea-wrapper {
|
#multi-input-wrapper {
|
||||||
position: absolute;
|
display: flex;
|
||||||
top: var(--title-height);
|
flex-direction: column;
|
||||||
bottom: 0;
|
height: calc(100% - var(--title-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
#input-tabs ul {
|
||||||
|
list-style: none;
|
||||||
|
background-color: var(--title-background-colour);
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
border-bottom: 1px solid var(--primary-border-colour);
|
||||||
|
height: var(--tab-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
#input-tabs ul li {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-width: 120px;
|
||||||
|
float: left;
|
||||||
|
padding: 0px;
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid var(--primary-border-colour);
|
||||||
|
height: var(--tab-height);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input-tabs ul li:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--primary-background-colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-input-tab {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-tab-content {
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
height: var(--tab-height);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close-tab {
|
||||||
|
height: var(--tab-height);
|
||||||
|
vertical-align: middle;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea-wrapper textarea,
|
.textarea-wrapper textarea,
|
||||||
|
@ -45,13 +102,21 @@
|
||||||
color: var(--fixed-width-font-colour);
|
color: var(--fixed-width-font-colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-input-area {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
#input-highlighter,
|
#input-highlighter,
|
||||||
#output-highlighter {
|
#output-highlighter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: calc(100% - var(--title-height));
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -61,6 +126,7 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#output-loader {
|
#output-loader {
|
||||||
|
@ -101,13 +167,13 @@
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
#input-file,
|
.input-file,
|
||||||
#output-file {
|
#output-file {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: calc(100% - var(--tab-height) - var(--title-height));
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue