Move status icon out of core grammar checking service
Status icon is user-implemented now.
This commit is contained in:
172
service.js
172
service.js
@@ -38,10 +38,12 @@ class BesService {
|
||||
* @param {Element} textElement The element in DOM tree that hosts coordinate-measurable clone of
|
||||
* the text to proof. Same as hostElement for <div>, separate for
|
||||
* <textarea> and <input> hosts.
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement, textElement) {
|
||||
constructor(hostElement, textElement, eventSink) {
|
||||
this.hostElement = hostElement
|
||||
this.textElement = textElement
|
||||
this.eventSink = eventSink
|
||||
this.results = [] // Results of grammar-checking, one per each block/paragraph of text
|
||||
this.createCorrectionPanel()
|
||||
|
||||
@@ -83,19 +85,20 @@ class BesService {
|
||||
* BesCKService.register for that.
|
||||
*
|
||||
* @param {Element} hostElement Host element
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
* @returns Grammar checking service registered for given DOM element; unfedined if no service
|
||||
* registered.
|
||||
*/
|
||||
static registerByElement(hostElement) {
|
||||
static registerByElement(hostElement, eventSink) {
|
||||
if (hostElement.tagName === 'TEXTAREA') {
|
||||
return BesTAService.register(hostElement)
|
||||
return BesTAService.register(hostElement, eventSink)
|
||||
} else if (
|
||||
hostElement.getAttribute('contenteditable')?.toLowerCase() ===
|
||||
'plaintext-only'
|
||||
) {
|
||||
return BesDOMPlainTextService.register(hostElement)
|
||||
return BesDOMPlainTextService.register(hostElement, eventSink)
|
||||
} else {
|
||||
return BesDOMService.register(hostElement)
|
||||
return BesDOMService.register(hostElement, eventSink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +119,8 @@ class BesService {
|
||||
)
|
||||
this.hostElement.spellcheck = this.originalSpellcheck
|
||||
this.clearCorrectionPanel()
|
||||
if (this.eventSink && 'unregister' in this.eventSink)
|
||||
this.eventSink.unregister(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,8 +150,9 @@ class BesService {
|
||||
this.proofingCount = 1 // Ref-count how many grammar-checking blocks of text are active
|
||||
this.proofingError = null // The first non-fatal error in grammar-checking run
|
||||
this.proofingMatches = 0 // Number of grammar mistakes detected in entire grammar-checking run
|
||||
this.updateStatusIcon('bes-status-loading', 'Besana preverja pravopis.')
|
||||
this.abortController = new AbortController()
|
||||
if (this.eventSink && 'startProofing' in this.eventSink)
|
||||
this.eventSink.startProofing(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,6 +160,8 @@ class BesService {
|
||||
*/
|
||||
onProofing() {
|
||||
this.proofingCount++
|
||||
if (this.eventSink && 'proofing' in this.eventSink)
|
||||
this.eventSink.proofing(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,10 +173,9 @@ class BesService {
|
||||
*/
|
||||
onFailedProofing(response) {
|
||||
delete this.abortController
|
||||
this.updateStatusIcon(
|
||||
'bes-status-error',
|
||||
`Pri preverjanju pravopisa je prišlo do napake ${response.status} ${response.statusText}.`
|
||||
)
|
||||
console.log(`Grammar checking failed: ${response.status} ${response.statusText}`)
|
||||
if (this.eventSink && 'failedProofing' in this.eventSink)
|
||||
this.eventSink.failedProofing(this, response)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,8 +184,13 @@ class BesService {
|
||||
* @param {Error} error Error
|
||||
*/
|
||||
onFailedProofingResult(error) {
|
||||
if (error !== 'AbortError' && !this.proofingError)
|
||||
this.proofingError = error
|
||||
if (error !== 'AbortError') {
|
||||
if (!this.proofingError)
|
||||
this.proofingError = error
|
||||
console.log(`Failed to parse grammar checking results: ${error}`)
|
||||
}
|
||||
if (this.eventSink && 'failedProofingResult' in this.eventSink)
|
||||
this.eventSink.failedProofingResult(this, error)
|
||||
if (--this.proofingCount <= 0) this.onEndProofing()
|
||||
}
|
||||
|
||||
@@ -189,6 +201,8 @@ class BesService {
|
||||
*/
|
||||
onProofingProgress(numberOfMatches) {
|
||||
this.proofingMatches += numberOfMatches
|
||||
if (this.eventSink && 'proofingProgress' in this.eventSink)
|
||||
this.eventSink.proofingProgress(this)
|
||||
if (--this.proofingCount <= 0) this.onEndProofing()
|
||||
}
|
||||
|
||||
@@ -197,17 +211,8 @@ class BesService {
|
||||
*/
|
||||
onEndProofing() {
|
||||
delete this.abortController
|
||||
if (this.proofingError) {
|
||||
this.updateStatusIcon(
|
||||
'bes-status-error',
|
||||
`Pri obdelavi odgovora pravopisnega strežnika je prišlo do napake: ${this.proofingError}`
|
||||
)
|
||||
} else if (this.proofingMatches > 0)
|
||||
this.updateStatusIcon(
|
||||
'bes-status-mistakes',
|
||||
`Število napak: ${this.proofingMatches}`
|
||||
)
|
||||
else this.updateStatusIcon('bes-status-success', 'V besedilu ni napak.')
|
||||
if (this.eventSink && 'endProofing' in this.eventSink)
|
||||
this.eventSink.endProofing(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +234,8 @@ class BesService {
|
||||
*/
|
||||
onReposition() {
|
||||
this.setCorrectionPanelSize()
|
||||
this.setStatusDivPosition()
|
||||
if (this.eventSink && 'reposition' in this.eventSink)
|
||||
this.eventSink.reposition(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,11 +243,9 @@ class BesService {
|
||||
*/
|
||||
onResize() {
|
||||
this.setCorrectionPanelSize()
|
||||
this.setStatusDivPosition()
|
||||
|
||||
// When window is resized, host element might resize too.
|
||||
// This may cause text to re-wrap requiring markup repositioning.
|
||||
this.repositionAllMarkup()
|
||||
if (this.eventSink && 'resize' in this.eventSink)
|
||||
this.eventSink.resize(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,17 +327,6 @@ class BesService {
|
||||
document.body.insertBefore(panelParent, document.body.firstChild)
|
||||
this.setCorrectionPanelSize()
|
||||
}
|
||||
|
||||
this.statusDiv = document.createElement('div')
|
||||
this.statusDiv.classList.add('bes-status-div')
|
||||
this.statusIcon = document.createElement('div')
|
||||
this.statusIcon.classList.add('bes-status-icon')
|
||||
this.statusDiv.appendChild(this.statusIcon)
|
||||
this.setStatusDivPosition()
|
||||
this.textElement.parentNode.insertBefore(
|
||||
this.statusDiv,
|
||||
this.textElement.nextSibling
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,8 +335,6 @@ class BesService {
|
||||
clearCorrectionPanel() {
|
||||
this.correctionPanel.remove()
|
||||
this.scrollPanel.remove()
|
||||
this.statusDiv.remove()
|
||||
this.statusIcon.remove()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,39 +438,6 @@ class BesService {
|
||||
this.proofAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* Repositions status DIV element.
|
||||
*/
|
||||
setStatusDivPosition() {
|
||||
const rect = this.textElement.getBoundingClientRect()
|
||||
const scrollbarWidth =
|
||||
this.textElement.offsetWidth - this.textElement.clientWidth
|
||||
this.statusDiv.style.left = `${rect.right - 40 - scrollbarWidth}px`
|
||||
const scrollbarHeight =
|
||||
this.textElement.offsetHeight - this.textElement.clientHeight
|
||||
this.statusDiv.style.top = `${rect.bottom - 30 - scrollbarHeight}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets status icon style and title.
|
||||
*
|
||||
* @param {String} status CSS class name to set status icon to
|
||||
* @param {String} title Title of the status icon
|
||||
*/
|
||||
updateStatusIcon(status, title) {
|
||||
const statuses = [
|
||||
'bes-status-loading',
|
||||
'bes-status-success',
|
||||
'bes-status-mistakes',
|
||||
'bes-status-error'
|
||||
]
|
||||
statuses.forEach(statusClass => {
|
||||
this.statusIcon.classList.remove(statusClass)
|
||||
})
|
||||
this.statusIcon.classList.add(status)
|
||||
this.statusDiv.title = title
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if host element is inline
|
||||
*
|
||||
@@ -520,9 +478,10 @@ class BesTreeService extends BesService {
|
||||
* @param {Element} textElement The element in DOM tree that hosts coordinate-measurable clone of
|
||||
* the text to proof. Same as hostElement for <div>, separate for
|
||||
* <textarea> and <input> hosts.
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement, textElement) {
|
||||
super(hostElement, textElement)
|
||||
constructor(hostElement, textElement, eventSink) {
|
||||
super(hostElement, textElement, eventSink)
|
||||
this.onClick = this.onClick.bind(this)
|
||||
this.textElement.addEventListener('click', this.onClick)
|
||||
}
|
||||
@@ -891,9 +850,10 @@ class BesDOMService extends BesTreeService {
|
||||
*
|
||||
* @param {Element} hostElement The element in DOM tree we are providing grammar-checking service
|
||||
* for
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement) {
|
||||
super(hostElement, hostElement)
|
||||
constructor(hostElement, eventSink) {
|
||||
super(hostElement, hostElement, eventSink)
|
||||
this.onBeforeInput = this.onBeforeInput.bind(this)
|
||||
this.hostElement.addEventListener('beforeinput', this.onBeforeInput)
|
||||
this.onInput = this.onInput.bind(this)
|
||||
@@ -904,12 +864,15 @@ class BesDOMService extends BesTreeService {
|
||||
* Registers grammar checking service.
|
||||
*
|
||||
* @param {Element} hostElement DOM element to register grammar checking service for
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
* @returns {BesDOMService} Grammar checking service instance
|
||||
*/
|
||||
static register(hostElement) {
|
||||
static register(hostElement, eventSink) {
|
||||
let service = BesService.getServiceByElement(hostElement)
|
||||
if (service) return service
|
||||
service = new BesDOMService(hostElement)
|
||||
service = new BesDOMService(hostElement, eventSink)
|
||||
if (service.eventSink && 'register' in service.eventSink)
|
||||
service.eventSink.register(service)
|
||||
service.proofAll()
|
||||
return service
|
||||
}
|
||||
@@ -974,9 +937,10 @@ class BesCKService extends BesTreeService {
|
||||
* @param {Element} hostElement The element in DOM tree we are providing grammar-checking service
|
||||
* for
|
||||
* @param {*} ckEditorInstance CKEditor instance
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement, ckEditorInstance) {
|
||||
super(hostElement, hostElement)
|
||||
constructor(hostElement, ckEditorInstance, eventSink) {
|
||||
super(hostElement, hostElement, eventSink)
|
||||
this.ckEditorInstance = ckEditorInstance
|
||||
this.disableCKEditorSpellcheck()
|
||||
this.onChangeData = this.onChangeData.bind(this)
|
||||
@@ -988,12 +952,15 @@ class BesCKService extends BesTreeService {
|
||||
*
|
||||
* @param {Element} hostElement DOM element to register grammar checking service for
|
||||
* @param {CKEditorInstance} ckEditorInstance Enable CKEditor tweaks
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
* @returns {BesCKService} Grammar checking service instance
|
||||
*/
|
||||
static register(hostElement, ckEditorInstance) {
|
||||
static register(hostElement, ckEditorInstance, eventSink) {
|
||||
let service = BesService.getServiceByElement(hostElement)
|
||||
if (service) return service
|
||||
service = new BesCKService(hostElement, ckEditorInstance)
|
||||
service = new BesCKService(hostElement, ckEditorInstance, eventSink)
|
||||
if (service.eventSink && 'register' in service.eventSink)
|
||||
service.eventSink.register(service)
|
||||
service.proofAll()
|
||||
return service
|
||||
}
|
||||
@@ -1104,14 +1071,6 @@ class BesCKService extends BesTreeService {
|
||||
this.proofAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* Repositions status DIV element.
|
||||
*/
|
||||
setStatusDivPosition() {
|
||||
this.statusDiv.style.right = `10px`
|
||||
this.statusDiv.style.bottom = `10px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if host element is inline
|
||||
*
|
||||
@@ -1139,9 +1098,10 @@ class BesPlainTextService extends BesService {
|
||||
* @param {Element} textElement The element in DOM tree that hosts coordinate-measurable clone of
|
||||
* the text to proof. Same as hostElement for <div>, separate for
|
||||
* <textarea> and <input> hosts.
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement, textElement) {
|
||||
super(hostElement, textElement)
|
||||
constructor(hostElement, textElement, eventSink) {
|
||||
super(hostElement, textElement, eventSink)
|
||||
this.reEOP = /(\r?\n){2,}/g
|
||||
this.onBeforeInput = this.onBeforeInput.bind(this)
|
||||
this.hostElement.addEventListener('beforeinput', this.onBeforeInput)
|
||||
@@ -1459,21 +1419,25 @@ class BesDOMPlainTextService extends BesPlainTextService {
|
||||
*
|
||||
* @param {Element} hostElement The element in DOM tree we are providing grammar-checking service
|
||||
* for
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement) {
|
||||
super(hostElement, hostElement)
|
||||
constructor(hostElement, eventSink) {
|
||||
super(hostElement, hostElement, eventSink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers grammar checking service.
|
||||
*
|
||||
* @param {Element} hostElement DOM element to register grammar checking service for
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
* @returns {BesDOMPlainTextService} Grammar checking service instance
|
||||
*/
|
||||
static register(hostElement) {
|
||||
static register(hostElement, eventSink) {
|
||||
let service = BesService.getServiceByElement(hostElement)
|
||||
if (service) return service
|
||||
service = new BesDOMPlainTextService(hostElement)
|
||||
service = new BesDOMPlainTextService(hostElement, eventSink)
|
||||
if (service.eventSink && 'register' in service.eventSink)
|
||||
service.eventSink.register(service)
|
||||
service.proofAll()
|
||||
return service
|
||||
}
|
||||
@@ -1594,9 +1558,10 @@ class BesTAService extends BesPlainTextService {
|
||||
*
|
||||
* @param {Element} hostElement The element in DOM tree we are providing grammar-checking service
|
||||
* for
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
*/
|
||||
constructor(hostElement) {
|
||||
super(hostElement, BesTAService.createTextElement(hostElement))
|
||||
constructor(hostElement, eventSink) {
|
||||
super(hostElement, BesTAService.createTextElement(hostElement), eventSink)
|
||||
this.textElement.replaceChildren(
|
||||
document.createTextNode(this.hostElement.value)
|
||||
)
|
||||
@@ -1606,12 +1571,15 @@ class BesTAService extends BesPlainTextService {
|
||||
* Registers grammar checking service.
|
||||
*
|
||||
* @param {Element} hostElement DOM element to register grammar checking service for
|
||||
* @param {*} eventSink Event sink for notifications
|
||||
* @returns {BesTAService} Grammar checking service instance
|
||||
*/
|
||||
static register(hostElement) {
|
||||
static register(hostElement, eventSink) {
|
||||
let service = BesService.getServiceByElement(hostElement)
|
||||
if (service) return service
|
||||
service = new BesTAService(hostElement)
|
||||
service = new BesTAService(hostElement, eventSink)
|
||||
if (service.eventSink && 'register' in service.eventSink)
|
||||
service.eventSink.register(service)
|
||||
service.proofAll()
|
||||
return service
|
||||
}
|
||||
|
Reference in New Issue
Block a user