diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index 5ab41fc..a416cb9 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -100,6 +100,7 @@ const Utils = new (class _Utils { */ getSelectedNode(){ const node = document.getSelection().focusNode + if(node === null) return null return (node.nodeType == 3 ? node.parentNode : node) } @@ -386,6 +387,24 @@ const Utils = new (class _Utils { dummy.remove() //sel.setPosition(sel.anchorNode, offset) } + + // TODO: Start using this with Utils.waitFor + /** + * Execute a function & await a promise in async context + * @param {*} input + * @param {*} promise + * @returns + */ + async asyncTrigger(input, promise){ + return new Promise((resolve, reject) => { + promise.then((...args) => { + resolve(...args) + }).catch((...args) => { + reject(...args) + }) + input() + }) + } }) // Math utilities (wrapper for MathQuill) @@ -537,6 +556,7 @@ const Math = new (class _Math { mathObject.image.remove() mathObject.container.remove() delete this.collection[id] + this.events.dispatchEvent(new CustomEvent("blur", { detail: mathObject })) return } @@ -610,10 +630,12 @@ class Editor { */ async setContent(data, id){ // UI compatibility + this.watchDocument = false this.target = { i: id } this.hook.innerHTML = "" + // eslint-disable-next-line no-constant-condition // Create construct let construct = [] @@ -671,6 +693,7 @@ class Editor { } // Write the elements to the editor + let addedContent = false for(let line of construct){ let lineElement = null for(let element of line){ @@ -685,16 +708,21 @@ class Editor { } if(line.length === 0) { lineElement = document.createElement("div") - if(window.browser === "firefox") lineElement.appendChild(document.createElement("br")) // Firefox line activator + lineElement.appendChild(document.createElement("br")) // line activator + } + if(lineElement) { + this.hook.appendChild(lineElement) + addedContent = true } - if(lineElement) this.hook.appendChild(lineElement) } + if(!addedContent) this.hook.innerHTML = `
${this.activator}
` // If no content was added, activate first line // Toggle all math for(let id in Math.collection){ - Math.open(id) - Math.close(id) + await Math.open(id) + await Math.close(id) } + this.watchDocument = true } /** @@ -741,7 +769,8 @@ class Editor { case "br": { // This is a manual line-break if(window.browser !== "firefox"){ // Does not mean anything on firefox - format.push("") + //
in a line by itself does not do anything on Chrome either + if(element.parentNode.childNodes.length !== 1) format.push("") } break } @@ -814,25 +843,23 @@ class Editor { } // Firefox patch: Make sure we are not in a math container - if(event.code === "Enter"){ - if(window.browser === "firefox"){ - const selection = document.getSelection() - const direction = selection.anchorOffset // 0 is left, 1 is right - if(selection.anchorNode.nodeName.toLowerCase() === "a" && selection.anchorNode.childNodes.length === 1 && selection.anchorNode.childNodes[0].nodeName.toLowerCase() === "img"){ - event.preventDefault() - if(direction === 0){ - const line = document.createElement("div") - line.appendChild(document.createElement("br")) // this.activator - this.activeLine.before(line) - Utils.selectByIndex(0, line) - }else { - const line = document.createElement("div") - line.appendChild(document.createElement("br")) // this.activator - this.activeLine.after(line) - Utils.selectByIndex(0, line) - } - return // Forced to return + if(event.code === "Enter" && window.browser === "firefox"){ + const selection = document.getSelection() + const direction = selection.anchorOffset // 0 is left, 1 is right + if(selection.anchorNode.nodeName.toLowerCase() === "a" && selection.anchorNode.childNodes.length === 1 && selection.anchorNode.childNodes[0].nodeName.toLowerCase() === "img"){ + event.preventDefault() + if(direction === 0){ + const line = document.createElement("div") + line.appendChild(document.createElement("br")) // this.activator + this.activeLine.before(line) + Utils.selectByIndex(0, line) + }else { + const line = document.createElement("div") + line.appendChild(document.createElement("br")) // this.activator + this.activeLine.after(line) + Utils.selectByIndex(0, line) } + return // Forced to return } } @@ -850,8 +877,21 @@ class Editor { Utils.selectByIndex(Utils.getNodeIndex(this.hook, newLine), this.hook) // Move into new line }else { // Create new line after current active line - Math.close(this.activeMathElement.id) - await Utils.waitForEvent(Math.events, "blur") + const id = this.activeMathElement.id + const offset = Utils.getNodeIndex(this.activeLine, this.activeMathElement.container) + await Utils.asyncTrigger(() => {Math.close(id)}, Utils.waitForEvent(Math.events, "blur")) + // Make sure the math element just closed still exists (may get deleted if empty) + if(Math.collection[id] === undefined) { + // Return caret to previous selection + // If line is empty, select the start + if(this.activeLine.childNodes.length === 1 && this.activeLine.childNodes[0].nodeName.toLowerCase() === "br"){ + Utils.selectByIndex(Utils.getNodeIndex(this.hook, this.activeLine), this.hook) + }else { + // Make sure the editor is not empty + if(this.hook.childNodes.length !== 0) Utils.selectByIndex(offset - 1, this.activeLine) + } + return + } // Make new line const newLine = document.createElement("div") this.activeLine.after(newLine) @@ -864,6 +904,19 @@ class Editor { return } + // Disable shift+Enter + if(event.key === "Enter" && this.activeMathElement === null){ + // Causes issues at least on chromium, prevention is required + event.preventDefault() + // Create a new line normally + const newLine = document.createElement("div") + newLine.innerHTML = "
" + this.activeLine.after(newLine) + this.activeLine = newLine + Utils.selectByIndex(Utils.getNodeIndex(this.hook, this.activeLine), this.hook) + console.debug("[ EDITOR ] Active line change to", this.activeLine) + } + // Arrow key control if(event.code === "ArrowLeft" || event.code === "ArrowRight"){ await Utils.toggleAnchorMode(Utils.getObjectPropertyArray(Math.collection, "container"), true) @@ -947,7 +1000,7 @@ class Editor { }) // Document content modification listener - const observerCallback = async () => { + const observerCallback = async e => { // Enable/disable if(!this.watchDocument) return @@ -960,6 +1013,13 @@ class Editor { Utils.selectByIndex(0, this.hook) } + // Text modified? What is activeLine? + const parentLine = e && e.addedNodes && e.addedNodes[0] !== null ? Utils.getParentLine() : null + if(parentLine && this.activeLine !== parentLine){ + this.activeLine = parentLine + console.debug("[ EDITOR ] Active line change to", this.activeLine) + } + // Firefox patch: If the image element is in the beginning/end of a line, remove textNodes from within the container if(window.browser === "firefox"){ for(const id in Math.collection){ @@ -1009,7 +1069,6 @@ class Editor { // Firefox patch: Detect useless br tags in empty lines if(window.browser === "firefox"){ if(this.activeMathElement !== null && this.activeMathElement.isOpen === false && this.activeMathElement.image !== null && this.activeMathElement.image.parentNode !== null){ - console.log(this.activeMathElement.image.parentNode.parentNode.childNodes) if(this.activeMathElement.image.parentNode.parentNode.childNodes[0].nodeName.toLowerCase() === "br" && this.activeMathElement.image.parentNode.parentNode.childNodes.length === 2){ this.activeMathElement.image.parentNode.parentNode.childNodes[0].remove() } @@ -1028,6 +1087,15 @@ class Editor { // Get selection data and make sure it's valid const selection = document.getSelection() const line = selection.anchorNode.parentElement === this.hook ? selection.anchorNode : Utils.getParentLine(selection.anchorNode) + + // If we have only one line in the editor, we can focus that (as there are no other options) + if(this.hook.childNodes.length === 1 && this.activeLine !== this.hook.childNodes[0]) { + this.activeLine = this.hook.childNodes[0] + Utils.selectByIndex(0, this.hook) + console.debug("[ EDITOR ] Active line change to", this.activeLine) + return + } + if(!Utils.isSomeParent(selection.anchorNode, this.hook)) return // Update active line @@ -1038,6 +1106,7 @@ class Editor { }) window.addEventListener("keydown", async event => { if(event.code === "ArrowUp" || event.code === "ArrowDown"){ + // Move with arrow keys const selection = document.getSelection() const line = selection.anchorNode.parentElement === this.hook ? selection.anchorNode : Utils.getParentLine(selection.anchorNode) if(!Utils.isSomeParent(selection.anchorNode, this.hook)) return