Work on js syntax highlighting in textarea

This commit is contained in:
Jonathan Crangle
2023-12-08 16:11:21 -05:00
parent 165ffcef89
commit c8a3c55ed6
2 changed files with 103 additions and 37 deletions

View File

@@ -2,9 +2,8 @@
// TODO: Parse JSON to YAML // TODO: Parse JSON to YAML
// TODO: Download YAML // TODO: Download YAML
// Injection scripts // Injection scripts
// TODO: Javascript syntax highlighting within textarea layout, styling and event listeners // TODO: Textarea handle events (tab key, shift tab, scroll)
// TODO: Textarea handlers and logic for javascript (e.g. tab, newline) // TODO: Javascript escaping/unescaping as required to prevent XSS and satisfy API requirements
// TODO: Javascript escaping/unescaping as required
// TODO: remove tailwind play cdn script in head of playground.html after syntax highlighting work complete // TODO: remove tailwind play cdn script in head of playground.html after syntax highlighting work complete
// Ninja Keys improvements // Ninja Keys improvements
// TODO: Group related items for Ninja Keys // TODO: Group related items for Ninja Keys
@@ -161,14 +160,14 @@ function getValues(id, description, params) {
function closeModal() { function closeModal() {
focusTrap.destroy(); focusTrap.destroy();
modalBody.removeEventListener("keydown", handleEscapeKey); modalBody.removeEventListener("keydown", handleKeyboardEvents);
modalBody.removeEventListener("keydown", handleEnterKey);
modalContainer.removeEventListener("click", handleClickOutside); modalContainer.removeEventListener("click", handleClickOutside);
modalSubmitButton.removeEventListener("click", closeModal); modalSubmitButton.removeEventListener("click", closeModal);
modalClose.removeEventListener("click", closeModal); modalClose.removeEventListener("click", closeModal);
inputEventListeners.forEach((listener, index) => { inputEventListeners.forEach((listener, index) => {
inputs[index].removeEventListener("input", listener); inputs[index].removeEventListener("input", listener);
}); });
modalContent.classList.remove("relative", "h-[220px]");
inputEventListeners.length = 0; inputEventListeners.length = 0;
inputs.length = 0; inputs.length = 0;
values = []; values = [];
@@ -182,13 +181,10 @@ function getValues(id, description, params) {
} }
} }
function handleEscapeKey(e) { function handleKeyboardEvents(e) {
if (e.key === "Escape") { if (e.key === "Escape") {
closeModal(); closeModal();
} }
}
function handleEnterKey(e) {
if (e.key === "Enter") { if (e.key === "Enter") {
if (e.target.tagName.toLowerCase() === "textarea") { if (e.target.tagName.toLowerCase() === "textarea") {
return; return;
@@ -196,20 +192,43 @@ function getValues(id, description, params) {
modalSubmitButton.click(); modalSubmitButton.click();
} }
} }
if (
e.key === "Tab" &&
!e.shiftKey &&
e.target.tagName.toLowerCase() === "textarea"
) {
e.preventDefault();
let text = e.target.value;
const start = e.target.selectionStart;
const end = e.target.selectionEnd;
e.target.value = text.substring(0, start) + "\t" + text.substring(end);
e.target.dispatchEvent(new Event("input"));
e.target.setSelectionRange(start + 1, start + 1);
}
} }
document.getElementById("modal-title").innerHTML = id; document.getElementById("modal-title").innerHTML = id;
document.getElementById("modal-description").innerHTML = description; document.getElementById("modal-description").innerHTML = description;
params.map((param, i) => { params.map((param, i) => {
function textareaEventListener(event) { function textareaEventListener(e) {
codeElement = document.querySelector("code"); codeElement = document.querySelector("code");
codeElement.innerText = event.target.value; let text = e.target.value;
Prism.highlightElement(codeElement);
values[i] = "|" + " " + event.target.value; if (text[text.length - 1] == "\n") {
text += " ";
} }
function inputEventListener(event) {
values[i] = event.target.value; codeElement.innerHTML = text
.replace(new RegExp("&", "g"), "&")
.replace(new RegExp("<", "g"), "&lt;");
Prism.highlightElement(codeElement);
values[i] = text;
}
function inputEventListener(e) {
values[i] = e.target.value;
} }
const label = document.createElement("label"); const label = document.createElement("label");
@@ -219,17 +238,39 @@ function getValues(id, description, params) {
if (param.name === "js") { if (param.name === "js") {
input = document.createElement("textarea"); input = document.createElement("textarea");
input.type = "textarea"; input.type = "textarea";
input.setAttribute("spellcheck", "false");
input.placeholder = "Enter your JavaScript injection code ...";
input.classList.add( input.classList.add(
"min-h-[200px]", "h-[200px]",
"w-full",
"font-mono", "font-mono",
"whitespace-break-spaces", "whitespace-break-spaces",
"font-semibold" "font-semibold",
"absolute",
"text-base",
"leading-6",
"rounded-md",
"ring-1",
"ring-slate-900/10",
"shadow-sm",
"z-10",
"p-4",
"m-0",
"my-2",
"bg-transparent",
"text-transparent",
"overflow-auto",
"resize-none",
"caret-white",
"hover:ring-slate-300",
"dark:bg-slate-800",
"dark:highlight-white/5",
"hyphens-none"
); );
input.style.tabSize = "4";
} else { } else {
input = document.createElement("input"); input = document.createElement("input");
input.type = "text"; input.type = "text";
}
input.id = `input-${i}`;
input.classList.add( input.classList.add(
"w-full", "w-full",
"text-sm", "text-sm",
@@ -242,16 +283,42 @@ function getValues(id, description, params) {
"py-1.5", "py-1.5",
"pl-2", "pl-2",
"pr-3", "pr-3",
"mt-0",
"hover:ring-slate-300", "hover:ring-slate-300",
"dark:bg-slate-800", "dark:bg-slate-800",
"dark:highlight-white/5" "dark:highlight-white/5",
"overflow-auto"
); );
}
input.id = `input-${i}`;
modalContent.appendChild(label); modalContent.appendChild(label);
modalContent.appendChild(input); modalContent.appendChild(input);
if (input.type === "textarea") { if (input.type === "textarea") {
label.classList.add("sr-only", "hidden");
preElement = document.createElement("pre"); preElement = document.createElement("pre");
codeElement = document.createElement("code"); codeElement = document.createElement("code");
preElement.setAttribute("aria-hidden", "true"); preElement.setAttribute("aria-hidden", "true");
preElement.classList.add(
"bg-[#2d2d2d]",
"h-[200px]",
"w-full",
"rounded-md",
"ring-1",
"ring-slate-900/10",
"shadow-sm",
"p-0",
"m-0",
"my-2",
"font-mono",
"text-base",
"leading-6",
"whitespace-break-spaces",
"font-semibold",
"absolute",
"z-0",
"overflow-auto"
);
modalContent.classList.add("relative", "h-[220px]");
preElement.setAttribute("tabindex", "-1"); preElement.setAttribute("tabindex", "-1");
codeElement.classList.add("language-javascript"); codeElement.classList.add("language-javascript");
preElement.appendChild(codeElement); preElement.appendChild(codeElement);
@@ -269,8 +336,7 @@ function getValues(id, description, params) {
document.getElementById("input-0").focus(); document.getElementById("input-0").focus();
return new Promise((resolve) => { return new Promise((resolve) => {
modalBody.addEventListener("keydown", handleEscapeKey); modalBody.addEventListener("keydown", handleKeyboardEvents);
modalBody.addEventListener("keydown", handleEnterKey);
modalContainer.addEventListener("click", handleClickOutside); modalContainer.addEventListener("click", handleClickOutside);
modalClose.addEventListener("click", () => { modalClose.addEventListener("click", () => {
closeModal(); closeModal();

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ladder | playground</title> <title>ladder | playground</title>
<link rel="stylesheet" href="/styles.css" /> <!-- <link rel="stylesheet" href="/styles.css" /> -->
<!-- <script src="https://cdn.tailwindcss.com"></script> --> <script src="https://cdn.tailwindcss.com"></script>
<style> <style>
#modifierContainer::-webkit-scrollbar { #modifierContainer::-webkit-scrollbar {
width: 8px; width: 8px;
@@ -416,7 +416,7 @@
<p id="modal-description">DESCRIPTION</p> <p id="modal-description">DESCRIPTION</p>
</div> </div>
<div <div
class="my-2 flex flex-col gap-2" class="my-2 flex flex-col gap-2 w-full"
id="modal-content" id="modal-content"
></div> ></div>
</div> </div>