From 9522ace2ce8fac83650394b018e51c8934b745b0 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Mon, 19 Feb 2024 16:14:03 +0100 Subject: [PATCH] Cleanups and improvements --- online-editor.js | 115 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 24 deletions(-) diff --git a/online-editor.js b/online-editor.js index 9b6d7b6..5860f3e 100644 --- a/online-editor.js +++ b/online-editor.js @@ -14,10 +14,10 @@ class BesEditor { this.proof(edit) edit.addEventListener('beforeinput', e => this.handleBeforeInput(e), false) edit.addEventListener('click', e => this.handleClick(e)) - edit.addEventListener('scroll', () => - this.handleScrollEvent(this.el, this.scrollPanel) + edit.addEventListener('scroll', e => + this.handleScrollEvent(edit, this.scrollPanel) ) - this.observeDeletions(this.el) + //this.observeDeletions(this.el) } // Register editor @@ -162,8 +162,18 @@ class BesEditor { } // Marks section of text that is about to change as not-yet-grammar-proofed. - handleBeforeInput() { + handleBeforeInput(event) { if (this.timer) clearTimeout(this.timer) + let blockElements = new Set() + event.getTargetRanges().forEach(range => { + BesEditor.getNodesInRange(range).forEach(el => + blockElements.add(this.getBlockParent(el)) + ) + }) + blockElements.forEach(block => { + this.clearProofed(block) + this.clearMistakeMarkup(block) + }) let editor = this this.timer = setTimeout(function () { editor.proof(editor.el) @@ -230,31 +240,35 @@ class BesEditor { // Mark given block element as not grammar-proofed. clearProofed(el) { - let filteredChildren = this.children.filter(child => child.elements === el) - if (filteredChildren.length) filteredChildren[0].isProofed = false + this.children + .filter(child => child.elements === el) + .forEach(child => { + child.isProofed = false + }) } // Remove all grammar mistakes markup for given block element. clearMistakeMarkup(el) { - let filteredChildren = this.children.filter(child => child.elements === el) - if (!filteredChildren.length) return - - // TODO: Remove elements that are found in editor object, that way we can avoid looping through all elements. - filteredChildren[0].matches.forEach(match => { - for (const rect of match.rects) { - for (let child of this.scrollPanel.children) { - let childRect = child.getBoundingClientRect() - const isWithinRect = - childRect.left >= rect.left && - childRect.right <= rect.right && - childRect.top >= rect.top && - childRect.bottom <= rect.bottom + 20 - if (isWithinRect) { - child.remove() + this.children + .filter(child => child.elements === el) + .forEach(child => { + // TODO: Remove elements that are found in editor object, that way we can avoid looping through all elements. + child.matches.forEach(match => { + for (const rect of match.rects) { + for (let child of this.scrollPanel.children) { + let childRect = child.getBoundingClientRect() + const isWithinRect = + childRect.left >= rect.left && + childRect.right <= rect.right && + childRect.top >= rect.top && + childRect.bottom <= rect.bottom + 20 + if (isWithinRect) { + child.remove() + } + } } - } - } - }) + }) + }) } // Adds grammar mistake markup @@ -302,6 +316,59 @@ class BesEditor { return el } + static getNextNode(node) { + if (node.firstChild) return node.firstChild + while (node) { + if (node.nextSibling) return node.nextSibling + node = node.parentNode + } + } + + static getParents(node) { + let parents = [] + do { + parents.push(node) + node = node.parentNode + } while (node) + return parents.reverse() + } + + static getNodesInRange(range) { + var start = range.startContainer + var end = range.endContainer + + let startAncestors = BesEditor.getParents(start) + let endAncestors = BesEditor.getParents(end) + let commonAncestor = null + for ( + let i = 0; + i < startAncestors.length && + i < endAncestors.length && + startAncestors[i] === endAncestors[i]; + ++i + ) { + commonAncestor = startAncestors[i] + } + + var nodes = [] + var node + + // walk parent nodes from start to common ancestor + for (node = start.parentNode; node; node = node.parentNode) { + nodes.push(node) + if (node == commonAncestor) break + } + nodes.reverse() + + // walk children and siblings from start until end is found + for (node = start; node; node = BesEditor.getNextNode(node)) { + nodes.push(node) + if (node == end) break + } + + return nodes + } + // TODO: Improve this function to support copied rich html content from news sites, etc. handleClick(e) { const targetEl = e.target