diff --git a/kalk_web/public/index.html b/kalk_web/public/index.html
index e9c9d71..c711417 100644
--- a/kalk_web/public/index.html
+++ b/kalk_web/public/index.html
@@ -3,7 +3,7 @@
-
+
Svelte app
@@ -16,6 +16,11 @@
background-color: #212121;
}
+ html,
+ body {
+ overflow-x: hidden;
+ }
+
.hint {
color: #9c9c9c;
}
diff --git a/kalk_web/src/KalkCalculator.svelte b/kalk_web/src/KalkCalculator.svelte
index 1825f09..e1b5f00 100644
--- a/kalk_web/src/KalkCalculator.svelte
+++ b/kalk_web/src/KalkCalculator.svelte
@@ -43,15 +43,24 @@
let kalkContext: Context;
let selectedLineOffset: number = 0;
let calculatorElement: HTMLElement;
- let inputElement: HTMLInputElement;
+ let inputElement: HTMLTextAreaElement;
+ let highlightedTextElement: HTMLElement;
+ let hasBeenInteractedWith = false;
- afterUpdate(() => {
- // Scroll to bottom
- outputElement.children[
- outputElement.children.length - 1
- ].scrollIntoView(false);
- calculatorElement.scrollIntoView();
- });
+ function setText(text: string) {
+ inputElement.value = text;
+ const highlighted = highlight(text);
+ setHtml(highlighted);
+ }
+
+ function setHtml(html: string) {
+ highlightedTextElement.innerHTML = html;
+ inputElement.value = highlightedTextElement.textContent;
+ }
+
+ function getHtml(): string {
+ return highlightedTextElement.innerHTML;
+ }
function calculate(
kalk: Kalk,
@@ -68,36 +77,44 @@
}
function handleKeyDown(event: KeyboardEvent, kalk: Kalk) {
+ hasBeenInteractedWith = true;
if (event.key == "Enter") {
selectedLineOffset = 0;
- const target = event.target as HTMLInputElement;
- const input = target.textContent;
+ const input = inputElement.value;
let output: string;
if (input.trim() == "help") {
output = `Link to usage guide`;
} else if (input.trim() == "clear") {
outputLines = [];
- target.innerHTML = "";
+ setText("");
return;
} else {
- const [result, success] = calculate(
- kalk,
- input.replace(/\s+/g, "") // Temporary fix, since it for some reason complains about spaces on chrome
- );
+ const [result, success] = calculate(kalk, input);
output = success
- ? highlight(result)[0]
+ ? highlight(result)
: `${result}`;
}
outputLines = output
- ? [...outputLines, [target.innerHTML, true], [output, false]]
- : [...outputLines, [target.innerHTML, true]];
+ ? [...outputLines, [getHtml(), true], [output, false]]
+ : [...outputLines, [getHtml(), true]];
- target.innerHTML = "";
+ setText("");
+
+ let i = 0;
+ setInterval(() => {
+ if (i == 60) return;
+ outputElement.children[
+ outputElement.children.length - 1
+ ].scrollIntoView();
+
+ calculatorElement.scrollIntoView(false);
+ i++;
+ }, 10);
}
}
@@ -106,12 +123,11 @@
// of the input field. This piece of code will put the cursor at the end,
// which therefore will need to be done afterwards, so that it doesn't just get moved back again.
if (event.key == "ArrowUp" || event.key == "ArrowDown") {
- const target = event.target as HTMLInputElement;
const change = event.key == "ArrowUp" ? 1 : -1;
selectedLineOffset += change;
if (selectedLineOffset < 0) {
- target.innerHTML = "";
+ setText("");
selectedLineOffset = 0;
return;
}
@@ -126,8 +142,7 @@
}
if (line) {
- target.innerHTML = line[0];
- setCursorPosEnd(target);
+ setHtml(line[0]);
}
if (selectedLineOffset >= outputLines.length) {
@@ -138,16 +153,27 @@
function handleInput(event: Event) {
const target = event.target as HTMLInputElement;
- const cursorPos = getCursorPos(target);
- const [highlighted, offset] = highlight(target.textContent);
- target.innerHTML = highlighted;
- setCursorPos(target, cursorPos - offset);
+ // Make sure it doesn't mess with the HTML.
+ target.value = target.value
+ .replaceAll("\n", "")
+ .replaceAll(" ", " ")
+ .replaceAll("&", "")
+ .replaceAll("<", "");
+ setText(target.value);
}
function handleTouchLine(event: Event) {
- if (!inputElement.innerHTML) {
+ if (!inputElement.value) {
const target = event.currentTarget as HTMLElement;
- inputElement.innerHTML = target.querySelector(".value").innerHTML;
+ setHtml(target.innerHTML);
+
+ // Sighs... What else?
+ let i = 0;
+ setInterval(() => {
+ if (i == 40) return;
+ inputElement.focus({ preventScroll: true });
+ i++;
+ }, 1);
}
}
@@ -158,128 +184,53 @@
}
function handleArrowClick(event: Event, left: boolean) {
- const target = event.target as HTMLElement;
- const cursorPos = getCursorPos(inputElement);
- target.blur();
- setCursorPos(inputElement, cursorPos + (left ? -1 : 1));
+ const length = inputElement.value.length;
+ const selection = inputElement.selectionEnd + (left ? -1 : 1);
+ inputElement.selectionEnd = Math.min(Math.max(selection, 0), length);
+ inputElement.selectionStart = inputElement.selectionEnd;
+ inputElement.focus({ preventScroll: true });
}
function insertText(input: string) {
- inputElement.focus({ preventScroll: true });
- let cursorPos = getCursorPos(inputElement);
- const textContent = inputElement.textContent;
- let movementOffset = input.length;
-
+ let offset = 0;
if (input == "(") {
input += ")";
} else if (input == "=") {
input = " = ";
- movementOffset = 3;
} else if (input == "Σ") {
input += "()";
- movementOffset = 2;
+ offset = -1;
} else if (input == "∫") {
input += "()";
- movementOffset = 2;
+ offset = -1;
} else if (input == "⌊") {
input += "⌋";
+ offset = -1;
} else if (input == "⌈") {
input += "⌉";
+ offset = -1;
} else if (input == ",") {
input = ", ";
- movementOffset = 2;
}
- const newString =
- textContent.slice(0, cursorPos) +
- input +
- textContent.slice(cursorPos);
- const [highlighted, offset] = highlight(newString);
-
- inputElement.innerHTML = highlighted;
+ inputElement.setRangeText(
+ input,
+ inputElement.selectionStart,
+ inputElement.selectionEnd,
+ "end"
+ );
+ inputElement.selectionEnd += offset;
+ setText(inputElement.value);
inputElement.focus({ preventScroll: true });
- setCursorPos(inputElement, cursorPos - offset + movementOffset);
-
- // I know this sucks, but it keeps scrolling away on some browsers >:(
- let i = 0;
- setInterval(() => {
- if (i == 60) return;
- calculatorElement.scrollIntoView();
- i++;
- }, 20);
}
- function focus(element: HTMLInputElement) {
+ function handleLoad(element: HTMLElement) {
if (autofocus) element.focus();
}
- function getCursorPos(element: HTMLInputElement): number {
- const shadowRoot = calculatorElement.getRootNode() as ShadowRoot;
- const range = shadow.getRange(shadowRoot);
- //const selection = shadowRoot.getSelection();
- //const range = selection.getRangeAt(0);
- const preCaretRange = range.cloneRange();
- preCaretRange.selectNodeContents(element);
- preCaretRange.setEnd(range.endContainer, range.endOffset);
-
- return preCaretRange.toString().length;
- }
-
- function setCursorPos(element: HTMLElement, indexToSelect: number) {
- const range = document.createRange();
- range.selectNodeContents(element);
- const textNodes = getTextNodesIn(element);
-
- let nodeEndPos = 0;
- for (const textNode of textNodes) {
- const previousNodeEndPos = nodeEndPos;
- nodeEndPos += textNode.length;
-
- // If the index that should be selected is
- // less than or equal to the current position (the end of the text node),
- // then the index points to somewhere inside the current text node.
- // This text node along with indexToSelect will then be used when setting the cursor position.
- if (indexToSelect <= nodeEndPos) {
- range.setStart(textNode, indexToSelect - previousNodeEndPos);
- range.setEnd(textNode, indexToSelect - previousNodeEndPos);
- break;
- }
- }
-
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
- }
-
- function setCursorPosEnd(element: HTMLElement) {
- const range = document.createRange();
- const selection = window.getSelection();
- range.selectNodeContents(element);
- range.setStart(element, range.endOffset);
- selection.removeAllRanges();
- selection.addRange(range);
- }
-
- function getTextNodesIn(node: Node): Text[] {
- const textNodes: Text[] = [];
-
- // If it's text node, add it to the list directly,
- // otherwise go through it recursively and find text nodes within it.
- if (node.nodeType == Node.TEXT_NODE) {
- textNodes.push(node as Text);
- } else {
- for (const child of node.childNodes) {
- textNodes.push(...getTextNodesIn(child));
- }
- }
-
- return textNodes;
- }
-
- function highlight(input: string): [string, number] {
- if (!input) return ["", 0];
+ function highlight(input: string): string {
+ if (!input) return "";
let result = input;
- let offset = 0;
result = result.replace(
/(?[^!-@\s_|^⌊⌋⌈⌉≈]+(_\d+)?)|(?[+\-/*%^!≈])/g,
(substring, identifier, _, op) => {
@@ -320,8 +271,6 @@
}
}
- offset += substring.length - newSubstring.length;
-
return `${newSubstring}`;
}
@@ -333,9 +282,7 @@
}
);
- if (result.endsWith(" ")) result = result.slice(0, -1) + " ";
-
- return [result, offset];
+ return result;
}
@@ -343,11 +290,11 @@
{#each outputLines as line}
-
+
{#if line[1]}
>>
{/if}
-
+
{@html line[0]}
@@ -358,22 +305,27 @@
{#await import("@paddim8/kalk")}
Loading...
{:then kalk}
-