Fix various issues
This commit is contained in:
parent
374dd0045a
commit
ef6615941b
@ -22,21 +22,4 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<style>
|
|
||||||
.bes-online-editor {
|
|
||||||
width: 80%;
|
|
||||||
height: 300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
line-height: 1.6;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
overflow-y: auto;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</html>
|
</html>
|
||||||
|
187
online-editor.js
187
online-editor.js
@ -2,9 +2,11 @@ const besUrl = 'http://localhost:225/api/v2/check'
|
|||||||
|
|
||||||
let besEditors = [] // Collection of all grammar checking services in the document
|
let besEditors = [] // Collection of all grammar checking services in the document
|
||||||
|
|
||||||
|
// TODO: Add support for <textarea>
|
||||||
|
|
||||||
class BesEditor {
|
class BesEditor {
|
||||||
constructor(edit, CKEditorInstance) {
|
constructor(edit, CKEditorInstance) {
|
||||||
this.el = edit
|
this.editorElement = edit
|
||||||
this.timer = null
|
this.timer = null
|
||||||
this.children = []
|
this.children = []
|
||||||
const { correctionPanel, scrollPanel } = this.createCorrectionPanel(edit)
|
const { correctionPanel, scrollPanel } = this.createCorrectionPanel(edit)
|
||||||
@ -12,9 +14,9 @@ class BesEditor {
|
|||||||
this.scrollPanel = scrollPanel
|
this.scrollPanel = scrollPanel
|
||||||
this.offsetTop = null
|
this.offsetTop = null
|
||||||
this.CKEditorInstance = CKEditorInstance
|
this.CKEditorInstance = CKEditorInstance
|
||||||
if (!this.CKEditorInstance) edit.classList.add('bes-online-editor')
|
|
||||||
this.originalSpellcheck = edit.spellcheck
|
this.originalSpellcheck = edit.spellcheck
|
||||||
edit.spellcheck = false
|
edit.spellcheck = false
|
||||||
|
this.abortController = new AbortController()
|
||||||
this.proof(edit)
|
this.proof(edit)
|
||||||
edit.addEventListener('beforeinput', BesEditor.handleBeforeInput, false)
|
edit.addEventListener('beforeinput', BesEditor.handleBeforeInput, false)
|
||||||
edit.addEventListener('click', BesEditor.handleClick)
|
edit.addEventListener('click', BesEditor.handleClick)
|
||||||
@ -37,60 +39,42 @@ class BesEditor {
|
|||||||
* Unregisters grammar checking service
|
* Unregisters grammar checking service
|
||||||
*/
|
*/
|
||||||
unregister() {
|
unregister() {
|
||||||
this.el.removeEventListener('scroll', BesEditor.handleScroll)
|
this.editorElement.removeEventListener('scroll', BesEditor.handleScroll)
|
||||||
this.el.removeEventListener('click', BesEditor.handleClick)
|
this.editorElement.removeEventListener('click', BesEditor.handleClick)
|
||||||
this.el.removeEventListener(
|
this.editorElement.removeEventListener(
|
||||||
'beforeinput',
|
'beforeinput',
|
||||||
BesEditor.handleBeforeInput,
|
BesEditor.handleBeforeInput,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
if (this.timer) clearTimeout(this.timer)
|
if (this.timer) clearTimeout(this.timer)
|
||||||
besEditors = besEditors.filter(item => item !== this)
|
besEditors = besEditors.filter(item => item !== this)
|
||||||
this.el.spellcheck = this.originalSpellcheck
|
this.editorElement.spellcheck = this.originalSpellcheck
|
||||||
this.el.classList.remove('bes-online-editor')
|
|
||||||
this.correctionPanel.remove()
|
this.correctionPanel.remove()
|
||||||
this.scrollPanel.remove()
|
this.scrollPanel.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add support for textarea elements
|
|
||||||
/**
|
/**
|
||||||
* Recursively grammar-proofs a DOM tree.
|
* Recursively grammar-proofs a DOM tree.
|
||||||
*
|
*
|
||||||
* @param {Node} el DOM root node to proof
|
* @param {Node} node DOM root node to proof
|
||||||
* @returns {Array} Markup of text to proof using BesStr
|
* @returns {Array} Markup of text to proof using BesStr
|
||||||
*/
|
*/
|
||||||
async proof(el) {
|
async proof(node) {
|
||||||
// If first child is not a block element, add a dummy <div>...</div> around it.
|
switch (node.nodeType) {
|
||||||
// This solution is still not fully tested and might need some improvements.
|
|
||||||
if (el.classList?.contains('bes-online-editor')) {
|
|
||||||
const firstChild = el.firstChild
|
|
||||||
if (
|
|
||||||
firstChild &&
|
|
||||||
(firstChild.nodeType === Node.TEXT_NODE ||
|
|
||||||
!BesEditor.isBlockElement(firstChild))
|
|
||||||
) {
|
|
||||||
const divEl = document.createElement('div')
|
|
||||||
if (firstChild.nodeType === Node.TEXT_NODE) {
|
|
||||||
divEl.textContent = firstChild.textContent
|
|
||||||
} else divEl.appendChild(firstChild.cloneNode(true))
|
|
||||||
el.insertBefore(divEl, firstChild)
|
|
||||||
el.removeChild(firstChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (el.nodeType) {
|
|
||||||
case Node.TEXT_NODE:
|
case Node.TEXT_NODE:
|
||||||
return [{ text: el.textContent, el: el, markup: false }]
|
return [{ text: node.textContent, node: node, markup: false }]
|
||||||
|
|
||||||
case Node.ELEMENT_NODE:
|
case Node.ELEMENT_NODE:
|
||||||
if (BesEditor.isBlockElement(el)) {
|
if (BesEditor.isBlockElement(node)) {
|
||||||
// Block elements are grammar-proofed independently.
|
// Block elements are grammar-proofed independently.
|
||||||
if (this.isProofed(el)) {
|
if (this.isProofed(node)) {
|
||||||
return [{ text: '<' + el.tagName + '/>', el: el, markup: true }]
|
return [
|
||||||
|
{ text: '<' + node.tagName + '/>', node: node, markup: true }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
this.clearMistakeMarkup(el)
|
this.clearMistakeMarkup(node)
|
||||||
let data = []
|
let data = []
|
||||||
for (const el2 of el.childNodes) {
|
for (const el2 of node.childNodes) {
|
||||||
data = data.concat(await this.proof(el2))
|
data = data.concat(await this.proof(el2))
|
||||||
}
|
}
|
||||||
if (data.some(x => !x.markup && !/^\s*$/.test(x.text))) {
|
if (data.some(x => !x.markup && !/^\s*$/.test(x.text))) {
|
||||||
@ -101,7 +85,7 @@ class BesEditor {
|
|||||||
x.markup ? { markup: x.text } : { text: x.text }
|
x.markup ? { markup: x.text } : { text: x.text }
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
language: el.lang ? el.lang : 'sl',
|
language: node.lang ? node.lang : 'sl',
|
||||||
level: 'picky'
|
level: 'picky'
|
||||||
}
|
}
|
||||||
const request = new Request(besUrl, {
|
const request = new Request(besUrl, {
|
||||||
@ -109,7 +93,8 @@ class BesEditor {
|
|||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
body: new URLSearchParams(requestData)
|
body: new URLSearchParams(requestData)
|
||||||
})
|
})
|
||||||
fetch(request)
|
const signal = this.abortController.signal
|
||||||
|
fetch(request, { signal })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
// TODO: Make connectivity and BesStr issues non-fatal. But show an error sign somewhere in the UI.
|
// TODO: Make connectivity and BesStr issues non-fatal. But show an error sign somewhere in the UI.
|
||||||
@ -134,7 +119,7 @@ class BesEditor {
|
|||||||
startingOffset + data[idx].text.length
|
startingOffset + data[idx].text.length
|
||||||
) {
|
) {
|
||||||
range.setStart(
|
range.setStart(
|
||||||
data[idx].el,
|
data[idx].node,
|
||||||
match.offset - startingOffset
|
match.offset - startingOffset
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@ -153,7 +138,7 @@ class BesEditor {
|
|||||||
/*startingOffset <= endOffset &&*/ endOffset <=
|
/*startingOffset <= endOffset &&*/ endOffset <=
|
||||||
startingOffset + data[idx].text.length
|
startingOffset + data[idx].text.length
|
||||||
) {
|
) {
|
||||||
range.setEnd(data[idx].el, endOffset - startingOffset)
|
range.setEnd(data[idx].node, endOffset - startingOffset)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,37 +152,34 @@ class BesEditor {
|
|||||||
match: match
|
match: match
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.markProofed(el, matches)
|
this.markProofed(node, matches)
|
||||||
|
|
||||||
// This is a solution for displaying mistakes in CKEditor. It is not the best solution, but it works for now.
|
|
||||||
if (this.CKEditorInstance) {
|
|
||||||
const resizeEvent = new Event('resize')
|
|
||||||
window.dispatchEvent(resizeEvent)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
if (error.name === 'AbortError') return
|
||||||
// TODO: Make parsing issues non-fatal. But show an error sign somewhere in the UI.
|
// TODO: Make parsing issues non-fatal. But show an error sign somewhere in the UI.
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Parsing backend server response failed: ' + error
|
'Parsing backend server response failed: ' + error
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return [{ text: '<' + el.tagName + '/>', el: el, markup: true }]
|
return [{ text: '<' + node.tagName + '/>', node: node, markup: true }]
|
||||||
} else {
|
} else {
|
||||||
// Surround inline element with dummy <tagName>...</tagName>.
|
// Surround inline element with dummy <tagName>...</tagName>.
|
||||||
let data = [{ text: '<' + el.tagName + '>', el: el, markup: true }]
|
let data = [
|
||||||
for (const el2 of el.childNodes) {
|
{ text: '<' + node.tagName + '>', node: node, markup: true }
|
||||||
|
]
|
||||||
|
for (const el2 of node.childNodes) {
|
||||||
data = data.concat(await this.proof(el2))
|
data = data.concat(await this.proof(el2))
|
||||||
}
|
}
|
||||||
data.splice(data.length, 0, {
|
data.splice(data.length, 0, {
|
||||||
text: '</' + el.tagName + '>',
|
text: '</' + node.tagName + '>',
|
||||||
markup: true
|
markup: true
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return [{ text: '<?' + el.nodeType + '>', el: el, markup: true }]
|
return [{ text: '<?' + node.nodeType + '>', node: node, markup: true }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,9 +208,10 @@ class BesEditor {
|
|||||||
*/
|
*/
|
||||||
static handleBeforeInput(event) {
|
static handleBeforeInput(event) {
|
||||||
const edit = event.target
|
const edit = event.target
|
||||||
let editor = besEditors.find(e => e.el === edit)
|
let editor = besEditors.find(e => e.editorElement === edit)
|
||||||
if (!editor) return
|
if (!editor) return
|
||||||
if (editor.timer) clearTimeout(editor.timer)
|
if (editor.timer) clearTimeout(editor.timer)
|
||||||
|
editor.abortController.abort()
|
||||||
let blockElements = new Set()
|
let blockElements = new Set()
|
||||||
event.getTargetRanges().forEach(range => {
|
event.getTargetRanges().forEach(range => {
|
||||||
BesEditor.getNodesInRange(range).forEach(el =>
|
BesEditor.getNodesInRange(range).forEach(el =>
|
||||||
@ -244,6 +227,7 @@ class BesEditor {
|
|||||||
editor.repositionMistakes()
|
editor.repositionMistakes()
|
||||||
}, 0)
|
}, 0)
|
||||||
editor.timer = setTimeout(function () {
|
editor.timer = setTimeout(function () {
|
||||||
|
editor.abortController = new AbortController()
|
||||||
editor.proof(edit)
|
editor.proof(edit)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
@ -255,7 +239,7 @@ class BesEditor {
|
|||||||
* @returns {Boolean} true if the element has already been grammar-proofed; false otherwise.
|
* @returns {Boolean} true if the element has already been grammar-proofed; false otherwise.
|
||||||
*/
|
*/
|
||||||
isProofed(el) {
|
isProofed(el) {
|
||||||
return this.children.find(child => child.elements === el)?.isProofed
|
return this.children.find(child => child.element === el)?.isProofed
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,9 +252,12 @@ class BesEditor {
|
|||||||
this.removeChild(el)
|
this.removeChild(el)
|
||||||
this.children.push({
|
this.children.push({
|
||||||
isProofed: true,
|
isProofed: true,
|
||||||
elements: el, // TODO: Rename "elements" to "el" - 1. It contains only single element (plural elements is misleading), 2. BesEditor also uses "el" named field for DOM matching.
|
element: el,
|
||||||
matches: matches
|
matches: matches
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This is a solution for displaying mistakes in CKEditor. It is not the best solution, but it works for now.
|
||||||
|
if (this.CKEditorInstance) window.dispatchEvent(new Event('resize'))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -279,12 +266,14 @@ class BesEditor {
|
|||||||
* @param {Element} el DOM element that we should re-grammar-proof
|
* @param {Element} el DOM element that we should re-grammar-proof
|
||||||
*/
|
*/
|
||||||
clearMistakeMarkup(el) {
|
clearMistakeMarkup(el) {
|
||||||
let child = this.children.find(child => child.elements === el)
|
let child = this.children.find(child => child.element === el)
|
||||||
if (!child) return
|
if (!child) return
|
||||||
child.isProofed = false
|
child.isProofed = false
|
||||||
child.matches.forEach(match => {
|
child.matches.forEach(match => {
|
||||||
match?.highlights.forEach(h => h.remove())
|
if (match?.highlights) {
|
||||||
delete match.highlights
|
match.highlights.forEach(h => h.remove())
|
||||||
|
delete match.highlights
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +283,7 @@ class BesEditor {
|
|||||||
* @param {Element} el DOM element for removal
|
* @param {Element} el DOM element for removal
|
||||||
*/
|
*/
|
||||||
removeChild(el) {
|
removeChild(el) {
|
||||||
this.children = this.children.filter(child => child.elements !== el)
|
this.children = this.children.filter(child => child.element !== el)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,7 +291,7 @@ class BesEditor {
|
|||||||
*/
|
*/
|
||||||
repositionMistakes() {
|
repositionMistakes() {
|
||||||
this.children.forEach(child => {
|
this.children.forEach(child => {
|
||||||
this.clearMistakeMarkup(child.elements)
|
this.clearMistakeMarkup(child.element)
|
||||||
child.matches.forEach(match => {
|
child.matches.forEach(match => {
|
||||||
const { clientRects, highlights } = this.addMistakeMarkup(match.range)
|
const { clientRects, highlights } = this.addMistakeMarkup(match.range)
|
||||||
match.rects = clientRects
|
match.rects = clientRects
|
||||||
@ -318,7 +307,6 @@ class BesEditor {
|
|||||||
* @returns {Object} Client rectangles and grammar mistake highlight elements
|
* @returns {Object} Client rectangles and grammar mistake highlight elements
|
||||||
*/
|
*/
|
||||||
addMistakeMarkup(range) {
|
addMistakeMarkup(range) {
|
||||||
// TODO: Consider using range.getClientRects() instead of range.getBoundingClientRect()
|
|
||||||
// In CKEditor case, the highlight element is not shown for some reason. But after resizing the window it is shown.
|
// In CKEditor case, the highlight element is not shown for some reason. But after resizing the window it is shown.
|
||||||
const clientRects = range.getClientRects()
|
const clientRects = range.getClientRects()
|
||||||
const scrollPanelRect = this.scrollPanel.getBoundingClientRect()
|
const scrollPanelRect = this.scrollPanel.getBoundingClientRect()
|
||||||
@ -343,7 +331,7 @@ class BesEditor {
|
|||||||
* Tests if given element is block element.
|
* Tests if given element is block element.
|
||||||
*
|
*
|
||||||
* @param {Element} el DOM element
|
* @param {Element} el DOM element
|
||||||
* @returns false if CSS display property is inline or inline-block; true otherwise.
|
* @returns false if CSS display property is inline; true otherwise.
|
||||||
*/
|
*/
|
||||||
static isBlockElement(el) {
|
static isBlockElement(el) {
|
||||||
switch (
|
switch (
|
||||||
@ -353,7 +341,6 @@ class BesEditor {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
) {
|
) {
|
||||||
case 'inline':
|
case 'inline':
|
||||||
case 'inline-block':
|
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
@ -367,7 +354,7 @@ class BesEditor {
|
|||||||
* @returns {Element} Innermost block element containing given node
|
* @returns {Element} Innermost block element containing given node
|
||||||
*/
|
*/
|
||||||
getBlockParent(el) {
|
getBlockParent(el) {
|
||||||
for (; el && el !== this.el; el = el.parentNode) {
|
for (; el && el !== this.editorElement; el = el.parentNode) {
|
||||||
if (el.nodeType === Node.ELEMENT_NODE && BesEditor.isBlockElement(el))
|
if (el.nodeType === Node.ELEMENT_NODE && BesEditor.isBlockElement(el))
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
@ -454,7 +441,7 @@ class BesEditor {
|
|||||||
*/
|
*/
|
||||||
static handleClick(event) {
|
static handleClick(event) {
|
||||||
const edit = BesEditor.findParent(event.target)
|
const edit = BesEditor.findParent(event.target)
|
||||||
let editor = besEditors.find(e => e.el === edit)
|
let editor = besEditors.find(e => e.editorElement === edit)
|
||||||
if (!editor) return
|
if (!editor) return
|
||||||
const target = editor.getBlockParent(event.target)
|
const target = editor.getBlockParent(event.target)
|
||||||
editor.renderPopup(target, event.clientX, event.clientY)
|
editor.renderPopup(target, event.clientX, event.clientY)
|
||||||
@ -469,13 +456,13 @@ class BesEditor {
|
|||||||
*/
|
*/
|
||||||
static handleScroll(event) {
|
static handleScroll(event) {
|
||||||
const edit = event.target
|
const edit = event.target
|
||||||
let editor = besEditors.find(e => e.el === edit)
|
let editor = besEditors.find(e => e.editorElement === edit)
|
||||||
if (!editor) return
|
if (!editor) return
|
||||||
editor.scrollPanel.style.top = -edit.scrollTop + 'px'
|
editor.scrollPanel.style.top = -edit.scrollTop + 'px'
|
||||||
editor.offsetTop = edit.scrollTop
|
editor.offsetTop = edit.scrollTop
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
editor.repositionMistakes()
|
editor.repositionMistakes()
|
||||||
}, 300)
|
}, 100)
|
||||||
// TODO: Move popup (if open) too.
|
// TODO: Move popup (if open) too.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,10 +474,7 @@ class BesEditor {
|
|||||||
*/
|
*/
|
||||||
static findParent(el) {
|
static findParent(el) {
|
||||||
for (; el; el = el.parentNode) {
|
for (; el; el = el.parentNode) {
|
||||||
if (
|
if (besEditors.find(editor => editor.editorElement === el)) {
|
||||||
el.classList?.contains('bes-online-editor') ||
|
|
||||||
el.classList?.contains('ck-editor__editable') // Find a better way to handle CKEditor
|
|
||||||
) {
|
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,7 +490,7 @@ class BesEditor {
|
|||||||
*/
|
*/
|
||||||
renderPopup(el, clientX, clientY) {
|
renderPopup(el, clientX, clientY) {
|
||||||
const popup = document.querySelector('bes-popup-el')
|
const popup = document.querySelector('bes-popup-el')
|
||||||
const matches = this.children.find(child => child.elements === el)?.matches
|
const matches = this.children.find(child => child.element === el)?.matches
|
||||||
if (matches) {
|
if (matches) {
|
||||||
for (let m of matches) {
|
for (let m of matches) {
|
||||||
if (m.rects) {
|
if (m.rects) {
|
||||||
@ -516,8 +500,7 @@ class BesEditor {
|
|||||||
m.match.replacements.forEach(replacement => {
|
m.match.replacements.forEach(replacement => {
|
||||||
popup.appendReplacements(
|
popup.appendReplacements(
|
||||||
el,
|
el,
|
||||||
r,
|
m,
|
||||||
m.match,
|
|
||||||
replacement.value,
|
replacement.value,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
@ -534,17 +517,15 @@ class BesEditor {
|
|||||||
|
|
||||||
// TODO: In rich HTML texts, match.offset has different value than in plain text.
|
// TODO: In rich HTML texts, match.offset has different value than in plain text.
|
||||||
// This function should be able to handle both cases or find a way that works for both.
|
// This function should be able to handle both cases or find a way that works for both.
|
||||||
static replaceText(el, rect, match, replacement, editor) {
|
replaceText(el, match, replacement) {
|
||||||
|
if (this.timer) clearTimeout(this.timer)
|
||||||
|
this.abortController.abort()
|
||||||
// const tags = this.getTagsAndText(el)
|
// const tags = this.getTagsAndText(el)
|
||||||
if (!editor.CKEditorInstance) {
|
if (!this.CKEditorInstance) {
|
||||||
const text = el.textContent
|
match.range.deleteContents()
|
||||||
const newText =
|
match.range.insertNode(document.createTextNode(replacement))
|
||||||
text.substring(0, match.offset) +
|
|
||||||
replacement +
|
|
||||||
text.substring(match.offset + match.length)
|
|
||||||
el.textContent = newText
|
|
||||||
} else {
|
} else {
|
||||||
const { CKEditorInstance } = editor
|
const { CKEditorInstance } = this
|
||||||
CKEditorInstance.model.change(writer => {
|
CKEditorInstance.model.change(writer => {
|
||||||
const viewElement =
|
const viewElement =
|
||||||
CKEditorInstance.editing.view.domConverter.mapDomToView(el)
|
CKEditorInstance.editing.view.domConverter.mapDomToView(el)
|
||||||
@ -552,15 +533,15 @@ class BesEditor {
|
|||||||
CKEditorInstance.editing.mapper.toModelElement(viewElement)
|
CKEditorInstance.editing.mapper.toModelElement(viewElement)
|
||||||
if (modelElement) {
|
if (modelElement) {
|
||||||
const elementRange = writer.createRangeIn(modelElement)
|
const elementRange = writer.createRangeIn(modelElement)
|
||||||
// TODO: This logic should work once the HTML tags are removed from match.offset and match.length if is possible.
|
// TODO: This logic should work once the HTML tags are removed from match.match.offset and match.match.length if is possible.
|
||||||
if (
|
if (
|
||||||
elementRange.start.offset <= match.offset &&
|
elementRange.start.offset <= match.match.offset &&
|
||||||
elementRange.end.offset >= match.offset + match.length
|
elementRange.end.offset >= match.match.offset + match.match.length
|
||||||
) {
|
) {
|
||||||
const start = writer.createPositionAt(modelElement, match.offset)
|
const start = writer.createPositionAt(modelElement, match.match.offset)
|
||||||
const end = writer.createPositionAt(
|
const end = writer.createPositionAt(
|
||||||
modelElement,
|
modelElement,
|
||||||
match.offset + match.length
|
match.match.offset + match.match.length
|
||||||
)
|
)
|
||||||
const range = writer.createRange(start, end)
|
const range = writer.createRange(start, end)
|
||||||
writer.remove(range)
|
writer.remove(range)
|
||||||
@ -569,11 +550,12 @@ class BesEditor {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
BesEditor.clearSingleMistake(editor, el, rect)
|
this.clearMistakeMarkup(el)
|
||||||
// In my opinion, this approach provides the most straightforward solution for repositioning mistakes after a change.
|
// In my opinion, this approach provides the most straightforward solution for repositioning mistakes after a change.
|
||||||
// It maintains reasonable performance as it only checks the block element that has been modified,
|
// It maintains reasonable performance as it only checks the block element that has been modified,
|
||||||
// rather than re-evaluating the entire document or a larger set of elements.
|
// rather than re-evaluating the entire document or a larger set of elements.
|
||||||
editor.proof(el)
|
this.abortController = new AbortController()
|
||||||
|
this.proof(el)
|
||||||
}
|
}
|
||||||
|
|
||||||
// static getTagsAndText(node) {
|
// static getTagsAndText(node) {
|
||||||
@ -590,25 +572,6 @@ class BesEditor {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// This function clears a single mistake
|
|
||||||
static clearSingleMistake(editor, el, rect) {
|
|
||||||
const childToDelete = editor.children.filter(
|
|
||||||
child => child.elements === el
|
|
||||||
)[0]
|
|
||||||
childToDelete.isProofed = false
|
|
||||||
childToDelete.matches = childToDelete.matches.filter(
|
|
||||||
match => !BesEditor.isPointInRect(rect.left, rect.top, match.rects[0])
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: find a better way to remove elements from the DOM
|
|
||||||
Array.from(editor.scrollPanel.children)
|
|
||||||
.filter(child => {
|
|
||||||
const childRect = child.getBoundingClientRect()
|
|
||||||
return BesEditor.isPointInRect(childRect.left, childRect.top, rect)
|
|
||||||
})
|
|
||||||
.forEach(child => child.remove())
|
|
||||||
}
|
|
||||||
|
|
||||||
setCorrectionPanelSize(editor, correctionPanel, scrollPanel) {
|
setCorrectionPanelSize(editor, correctionPanel, scrollPanel) {
|
||||||
const styles = window.getComputedStyle(editor)
|
const styles = window.getComputedStyle(editor)
|
||||||
const totalWidth =
|
const totalWidth =
|
||||||
@ -648,12 +611,12 @@ window.onload = () => {
|
|||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
besEditors.forEach(editor => {
|
besEditors.forEach(editor => {
|
||||||
editor.setCorrectionPanelSize(
|
editor.setCorrectionPanelSize(
|
||||||
editor.el,
|
editor.editorElement,
|
||||||
editor.correctionPanel,
|
editor.correctionPanel,
|
||||||
editor.scrollPanel
|
editor.scrollPanel
|
||||||
)
|
)
|
||||||
editor.children.forEach(child => {
|
editor.children.forEach(child => {
|
||||||
editor.clearMistakeMarkup(child.elements)
|
editor.clearMistakeMarkup(child.element)
|
||||||
child.matches.forEach(match => {
|
child.matches.forEach(match => {
|
||||||
const { clientRects, highlights } = editor.addMistakeMarkup(match.range)
|
const { clientRects, highlights } = editor.addMistakeMarkup(match.range)
|
||||||
match.rects = clientRects
|
match.rects = clientRects
|
||||||
@ -775,14 +738,14 @@ class BesPopupEl extends HTMLElement {
|
|||||||
this.shadowRoot.querySelector('.popup-text').textContent = text
|
this.shadowRoot.querySelector('.popup-text').textContent = text
|
||||||
}
|
}
|
||||||
|
|
||||||
appendReplacements(el, rect, match, replacement, editor) {
|
appendReplacements(el, match, replacement, editor) {
|
||||||
const replacementDiv = this.shadowRoot.querySelector('.bes-replacement-div')
|
const replacementDiv = this.shadowRoot.querySelector('.bes-replacement-div')
|
||||||
const replacementBtn = document.createElement('button')
|
const replacementBtn = document.createElement('button')
|
||||||
replacementBtn.classList.add('bes-replacement-btn')
|
replacementBtn.classList.add('bes-replacement-btn')
|
||||||
replacementBtn.textContent = replacement
|
replacementBtn.textContent = replacement
|
||||||
replacementBtn.classList.add('bes-replacement')
|
replacementBtn.classList.add('bes-replacement')
|
||||||
replacementBtn.addEventListener('click', () => {
|
replacementBtn.addEventListener('click', () => {
|
||||||
BesEditor.replaceText(el, rect, match, replacement, editor)
|
editor.replaceText(el, match, replacement)
|
||||||
})
|
})
|
||||||
replacementDiv.appendChild(replacementBtn)
|
replacementDiv.appendChild(replacementBtn)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user