diff --git a/CONTRIBUTORS b/CONTRIBUTORS index c22bc764a..e9e28969b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -234,6 +234,7 @@ Emmanuel Ferdman Sunong2008 Marvin Kopf Kevin Nakamura +Felipe Colona ******************** diff --git a/ts/lib/sveltelib/input-handler.ts b/ts/lib/sveltelib/input-handler.ts index a24a9338e..c6ea73e9f 100644 --- a/ts/lib/sveltelib/input-handler.ts +++ b/ts/lib/sveltelib/input-handler.ts @@ -49,7 +49,7 @@ export interface InputHandlerAPI { export function getMaxOffset(node: Node) { if (node.nodeType === Node.TEXT_NODE) { - if(!node.textContent) return 0; + if (!node.textContent) { return 0; } return node.textContent.length; } else if (node.nodeType === Node.ELEMENT_NODE) { return node.childNodes.length; @@ -57,35 +57,34 @@ export function getMaxOffset(node: Node) { return 0; } - -function getCaretPosition(element: Element){ +function getCaretPosition(element: Element) { const selection = getSelection(element)!; const range = getRange(selection); - if(!range) return 0; + if (!range) { return 0; } let startNode = range.startContainer; let startOffset = range.startOffset; - if(!range.collapsed){ - if(selection.anchorNode) startNode = selection.anchorNode; + if (!range.collapsed) { + if (selection.anchorNode) { startNode = selection.anchorNode; } startOffset = selection.anchorOffset; } - if(startNode.nodeType == Node.TEXT_NODE){ + if (startNode.nodeType == Node.TEXT_NODE) { let counter = 0; - for(const node of element.childNodes){ - if(node === startNode) break; - if(node.textContent && node.nodeType == Node.TEXT_NODE) counter += node.textContent.length; - if(node.nodeName === "BR") counter++; + for (const node of element.childNodes) { + if (node === startNode) { break; } + if (node.textContent && node.nodeType == Node.TEXT_NODE) { counter += node.textContent.length; } + if (node.nodeName === "BR") { counter++; } } counter += startOffset; return counter; } else { let counter = 0; - for(let i = 0; (i < startOffset) && (i < element.childNodes.length); i++){ + for (let i = 0; (i < startOffset) && (i < element.childNodes.length); i++) { const node = element.childNodes[i]; - if(node.textContent && node.nodeType == Node.TEXT_NODE) counter += node.textContent.length; - if(node.nodeName === "BR") counter++; + if (node.textContent && node.nodeType == Node.TEXT_NODE) { counter += node.textContent.length; } + if (node.nodeName === "BR") { counter++; } } return counter; } @@ -107,12 +106,12 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] { const config = { attributes: true, childList: true, - subtree: true + subtree: true, }; const observer = new MutationObserver(onMutation); - function onMutation(mutationsList: MutationRecord[], observer){ - const element = mutationsList[0].target; + function onMutation(mutationsList: MutationRecord[]) { + const element = mutationsList[0].target; undoManager.register(element.innerHTML, getMaxOffset(element)); } @@ -150,12 +149,12 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] { } async function onInput(this: Element, event: Event): Promise { - if(!hasSetupObserver) { + if (!hasSetupObserver) { observer.observe(this, config); hasSetupObserver = true; } const position = getCaretPosition(this); - undoManager.register(this.innerHTML, position-1); + undoManager.register(this.innerHTML, position - 1); undoManager.clearRedoStack(); await afterInput.dispatch({ event }); } @@ -186,12 +185,10 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] { specialKey.dispatch({ event, action: "enter" }); } else if (event.code === "Tab") { specialKey.dispatch({ event, action: "tab" }); - } - else if((event.ctrlKey || event.metaKey) && event.key == "z"){ + } else if ((event.ctrlKey || event.metaKey) && event.key == "z") { event.preventDefault(); undoManager.undo(this); - } - else if((event.ctrlKey || event.metaKey) && event.key == "y"){ + } else if ((event.ctrlKey || event.metaKey) && event.key == "y") { event.preventDefault(); undoManager.redo(this); } diff --git a/ts/lib/sveltelib/undo-manager.ts b/ts/lib/sveltelib/undo-manager.ts index c8ebdb59d..b475dd17c 100644 --- a/ts/lib/sveltelib/undo-manager.ts +++ b/ts/lib/sveltelib/undo-manager.ts @@ -4,7 +4,7 @@ import { getMaxOffset } from "./input-handler"; type State = { content: string; position: number; -} +}; export class UndoManager { private undoStack: State[] = []; @@ -13,34 +13,34 @@ export class UndoManager { private transactionStart: number = 0; public register = this.debounce(this.pushToUndo, 500, (position: number) => this.transactionStart = position); - public clearRedoStack(){ - if(this.isUpdating) return; + public clearRedoStack() { + if (this.isUpdating) { return; } this.redoStack = []; } public pushToUndo(content: string): void { - if(this.isUpdating) return; - if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length-1].content === content) return; + if (this.isUpdating) { return; } + if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length - 1].content === content) { return; } - const state = {content, position: this.transactionStart} + const state = { content, position: this.transactionStart }; this.undoStack.push(state); } private pushToRedo(content: string): void { - if (this.redoStack.length > 0 && this.redoStack[this.redoStack.length-1].content === content) return; + if (this.redoStack.length > 0 && this.redoStack[this.redoStack.length - 1].content === content) { return; } - const state = {content: content, position: this.transactionStart} + const state = { content: content, position: this.transactionStart }; this.redoStack.push(state); } - public undo(element: Element): void{ + public undo(element: Element): void { this.isUpdating = true; const undoedState = this.undoStack.pop(); - if(undoedState) this.pushToRedo(undoedState.content); + if (undoedState) { this.pushToRedo(undoedState.content); } let last: State; - if(this.undoStack.length <= 0) last = {content: "", position: 0}; - else last = this.undoStack[this.undoStack.length-1]; + if (this.undoStack.length <= 0) { last = { content: "", position: 0 }; } + else { last = this.undoStack[this.undoStack.length - 1]; } element.innerHTML = last.content; const selection = getSelection(element)!; @@ -49,42 +49,44 @@ export class UndoManager { let counter = this.transactionStart; let nodeFound: Node | null = null; let nodeOffset = 0; - for(const node of element.childNodes){ + for (const node of element.childNodes) { let nodeLength = node.textContent?.length || 0; if (counter <= nodeLength) { nodeFound = node; nodeOffset = counter; break; } - if(node.nodeType !== Node.TEXT_NODE) counter--; + if (node.nodeType !== Node.TEXT_NODE) { counter--; } counter -= nodeLength; } - if(!range){ + if (!range) { this.isUpdating = false; return; } - if(!nodeFound){ - if(element.lastChild) range?.setStart(element.lastChild as Node, (element.lastChild?.textContent?.length || 0)) + if (!nodeFound) { + if (element.lastChild) { + range?.setStart(element.lastChild as Node, element.lastChild?.textContent?.length || 0); + } range.collapse(true); - selection.removeAllRanges() + selection.removeAllRanges(); selection.addRange(range); this.isUpdating = false; return; } let finalOffset = Math.min(nodeOffset, nodeFound.textContent?.length || 0); - if(finalOffset > getMaxOffset(nodeFound)) finalOffset = getMaxOffset(nodeFound); + if (finalOffset > getMaxOffset(nodeFound)) { finalOffset = getMaxOffset(nodeFound); } range.setStart(nodeFound, finalOffset); range.collapse(true); - selection.removeAllRanges() + selection.removeAllRanges(); selection.addRange(range); - if(this.undoStack.length > 0) this.transactionStart = this.undoStack[this.undoStack.length-1].position; + if (this.undoStack.length > 0) { this.transactionStart = this.undoStack[this.undoStack.length - 1].position; } this.isUpdating = false; } public redo(element: Element): void { const redoedState = this.redoStack.pop(); - if(!redoedState) return; + if (!redoedState) { return; } this.transactionStart = redoedState.position; this.pushToUndo(redoedState.content); @@ -97,36 +99,38 @@ export class UndoManager { let counter = this.transactionStart; let nodeFound: Node | null = null; let nodeOffset = 0; - for(const node of element.childNodes){ + for (const node of element.childNodes) { let nodeLength = node.textContent?.length || 0; if (counter <= nodeLength) { nodeFound = node; nodeOffset = counter; break; } - if(node.nodeName === "BR") counter--; + if (node.nodeName === "BR") { counter--; } counter -= nodeLength; } - if(!range){ + if (!range) { this.isUpdating = false; return; } - if(!nodeFound){ - if(element.lastChild) range?.setStart(element.lastChild as Node, (element.lastChild?.textContent?.length || 0)) + if (!nodeFound) { + if (element.lastChild) { + range?.setStart(element.lastChild as Node, element.lastChild?.textContent?.length || 0); + } range.collapse(true); - selection.removeAllRanges() + selection.removeAllRanges(); selection.addRange(range); this.isUpdating = false; return; } let finalOffset = Math.min(nodeOffset, nodeFound.textContent?.length || 0); - if(finalOffset > getMaxOffset(nodeFound)) finalOffset = getMaxOffset(nodeFound); + if (finalOffset > getMaxOffset(nodeFound)) { finalOffset = getMaxOffset(nodeFound); } range.setStart(nodeFound, finalOffset); range.collapse(true); - selection.removeAllRanges() + selection.removeAllRanges(); selection.addRange(range); - if(this.redoStack.length > 0) this.transactionStart = this.redoStack[this.redoStack.length-1].position; + if (this.redoStack.length > 0) { this.transactionStart = this.redoStack[this.redoStack.length - 1].position; } this.isUpdating = false; } @@ -135,7 +139,7 @@ export class UndoManager { return (...args) => { const isNewTransaction = timeout === undefined; clearTimeout(timeout); - if(isNewTransaction) onTransactionStart.call(this, args[1]) + if (isNewTransaction) { onTransactionStart.call(this, args[1]); } timeout = setTimeout(() => { func.call(this, args[0]); @@ -143,4 +147,4 @@ export class UndoManager { }, delay); }; } -} \ No newline at end of file +}