Move status icon out of core grammar checking service
Status icon is user-implemented now.
@ -9,6 +9,7 @@
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/41.1.0/classic/ckeditor.js"></script>
|
||||
<script>const besUrl = 'http://localhost:225/api/v2';</script>
|
||||
<script src="../service.js"></script>
|
||||
<script src="common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p class="my-block">This is an example of a CKEditor edit control. Edit the text, resize the control or browser window, scroll around, click...</p>
|
||||
@ -22,7 +23,7 @@
|
||||
<bes-popup-el/>
|
||||
<script>
|
||||
ClassicEditor.create(document.querySelector('#editor'))
|
||||
.then(newEditor => BesCKService.register(newEditor.ui.view.editable.element, newEditor))
|
||||
.then(newEditor => BesCKService.register(newEditor.ui.view.editable.element, newEditor, new BesCKStatusIconEventSink()))
|
||||
.catch(error => console.error(error))
|
||||
</script>
|
||||
</body>
|
||||
|
140
samples/common.js
Normal file
@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Class to receive grammar checking service notification
|
||||
*
|
||||
* This implementation handles the status icon in the lower-right corner of the host element.
|
||||
*/
|
||||
class BesStatusIconEventSink {
|
||||
/**
|
||||
* Called when grammar checking service is registered
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
register(service) {
|
||||
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(service)
|
||||
service.textElement.parentNode.insertBefore(
|
||||
this.statusDiv,
|
||||
service.textElement.nextSibling
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when grammar checking service is unregistered
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
unregister(service) {
|
||||
this.statusDiv.remove()
|
||||
this.statusIcon.remove()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report repositioning
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
reposition(service) {
|
||||
this.setStatusDivPosition(service)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report resizing
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
resize(service) {
|
||||
this.setStatusDivPosition(service)
|
||||
}
|
||||
|
||||
/**
|
||||
* Repositions status DIV element.
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
setStatusDivPosition(service) {
|
||||
const rect = service.textElement.getBoundingClientRect()
|
||||
const scrollbarWidth =
|
||||
service.textElement.offsetWidth - service.textElement.clientWidth
|
||||
this.statusDiv.style.left = `${rect.right - 40 - scrollbarWidth}px`
|
||||
const scrollbarHeight =
|
||||
service.textElement.offsetHeight - service.textElement.clientHeight
|
||||
this.statusDiv.style.top = `${rect.bottom - 30 - scrollbarHeight}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report grammar proofing started
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
startProofing(service) {
|
||||
this.updateStatusIcon('bes-status-loading', 'Besana preverja pravopis.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report grammar-checking failed (as 500 Internal server error, timeout, etc.)
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
* @param {Response} response HTTP response
|
||||
*/
|
||||
failedProofing(service, response) {
|
||||
this.updateStatusIcon(
|
||||
'bes-status-error',
|
||||
`Pri preverjanju pravopisa je prišlo do napake ${response.status} ${response.statusText}.`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report grammar-checking run is ended
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
endProofing(service) {
|
||||
if (service.proofingError) {
|
||||
this.updateStatusIcon(
|
||||
'bes-status-error',
|
||||
`Pri obdelavi odgovora pravopisnega strežnika je prišlo do napake: ${service.proofingError}`
|
||||
)
|
||||
} else if (service.proofingMatches > 0)
|
||||
this.updateStatusIcon(
|
||||
'bes-status-mistakes',
|
||||
`Število napak: ${service.proofingMatches}`
|
||||
)
|
||||
else this.updateStatusIcon('bes-status-success', 'V besedilu ni napak.')
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
class BesCKStatusIconEventSink extends BesStatusIconEventSink {
|
||||
/**
|
||||
* Repositions status DIV element.
|
||||
*
|
||||
* @param {BesService} service Grammar checking service
|
||||
*/
|
||||
setStatusDivPosition(service) {
|
||||
this.statusDiv.style.right = `10px`
|
||||
this.statusDiv.style.bottom = `10px`
|
||||
}
|
||||
}
|
@ -8,10 +8,11 @@
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<script>const besUrl = 'http://localhost:225/api/v2';</script>
|
||||
<script src="../service.js"></script>
|
||||
<script src="common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p class="my-block">This is an example of a simple <code><div contenteditable="plaintext-only"></code> edit control. Edit the text, resize the control or browser window, scroll around, click...</p>
|
||||
<div class="my-block my-control bes-service" contenteditable="plaintext-only">Tukaj vpišite besedilo ki ga želite popraviti.
|
||||
<div id="my-editor" class="my-block my-control" contenteditable="plaintext-only">Tukaj vpišite besedilo ki ga želite popraviti.
|
||||
|
||||
Prišla je njena lepa hčera. Smatram da tega nebi bilo potrebno storiti. Predavanje je trajalo dve ure. S njim grem v Kamnik. Janez jutri nebo prišel. Prišel je z 100 idejami.
|
||||
|
||||
@ -21,5 +22,8 @@ Na mizo nisem položil knjigo.
|
||||
|
||||
Kvazimodo ji je ponavadi prinesel hrano in pijačo, medtem ko je spala, da ne bi videla njegov iznakažen in grd obraz. Poleg tega ji je pustil tudi piščalko, da bi ga lahko priklicala, če bi bilo to potrebno. Kvazimodo se je odločil, da razveseli Esmeraldo in ji obljubi, da ji bo pripeljal Febusa. Toda Febus ni želel priti. Kvazimodo ji je raje lagal, da ni mogel najti Febusa, kot da Esmeraldi pove resnico, ker bi ona trpela.</div>
|
||||
<bes-popup-el/>
|
||||
<script>
|
||||
BesService.registerByElement(document.getElementById('my-editor'), new BesStatusIconEventSink())
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -8,10 +8,11 @@
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<script>const besUrl = 'http://localhost:225/api/v2';</script>
|
||||
<script src="../service.js"></script>
|
||||
<script src="common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p class="my-block">This is an example of a simple <code><div contenteditable="true"></code> edit control. Edit the text, resize the control or browser window, scroll around, click...</p>
|
||||
<div class="my-block my-control bes-service" contenteditable="true">
|
||||
<div id="my-editor" class="my-block my-control" contenteditable="true">
|
||||
<p>Tukaj vpišite besedilo ki ga želite popraviti.</p>
|
||||
<p>Prišla je njena lepa hčera. Smatram da tega nebi bilo potrebno storiti. Predavanje je trajalo dve ure. S njim grem v Kamnik. Janez jutri nebo prišel. Prišel je z 100 idejami.</p>
|
||||
<p>To velja tudi v Bledu. To se je zgodilo na velikemu vrtu. Prišel je na Kamnik. On je včeraj prišel z svojo torbo. Dve žemlje prosim. Pogosto brskam po temu forumu. Prišel je včeraj in sicer s otroci. To ne vem. Pogleda vse kar daš v odložišče. Nisem jo videl. Ona izgleda dobro. Pri zanikanju ne smete uporabljati tožilnik. Vlak gre v Ljubljano čez Zidani Most. Skočil je čez okno. Slovenija meji na avstrijo. Jaz pišem v Slovenščini vsak Torek. Novica, da je skupina 25 planincev hodila pod vodstvom gorskega vodnika je napačna in zavajujoča. Želim da poješ kosmizailo. Jaz pogosto brskam po temu forumu. Med tem ko je iskal ključe, so se odprla vrata. V takoimenovanem skladišču je bilo veliko ljudi. V sobi sta dve mize. Stekel je h mami. Videl sem Jurčič Micko. To je bil njegov življenski cilj. Po vrsti popravite vse kar želite. Preden zaspiva mi prebere pravljico. Prišel je s stricom. Oni zadanejo tarčo. Mi gremo teči po polju. Mi gremo peči kruh. Usedel se je k miza. Postreži kosilo! Skul je veslanje z dvemi vesli.</p>
|
||||
@ -19,5 +20,8 @@
|
||||
<p>Kvazimodo ji je ponavadi prinesel hrano in pijačo, medtem ko je spala, da ne bi videla njegov iznakažen in grd obraz. Poleg tega ji je pustil tudi piščalko, da bi ga lahko priklicala, če bi bilo to potrebno. Kvazimodo se je odločil, da razveseli Esmeraldo in ji obljubi, da ji bo pripeljal Febusa. Toda Febus ni želel priti. Kvazimodo ji je raje lagal, da ni mogel najti Febusa, kot da Esmeraldi pove resnico, ker bi ona trpela.</p>
|
||||
</div>
|
||||
<bes-popup-el/>
|
||||
<script>
|
||||
BesService.registerByElement(document.getElementById('my-editor'), new BesStatusIconEventSink())
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -9,6 +9,7 @@
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/41.1.0/classic/ckeditor.js"></script>
|
||||
<script>const besUrl = 'http://localhost:225/api/v2';</script>
|
||||
<script src="../service.js"></script>
|
||||
<script src="common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p class="my-block">This is an example how to implement dynamic registration and deregistration of a grammar checking service on supported HTML controls.</p>
|
||||
@ -16,11 +17,11 @@
|
||||
<script>
|
||||
function toggle_grammar_service(el) {
|
||||
if (el.checked) {
|
||||
BesService.registerByElement(document.getElementById('textarea-control'))
|
||||
BesService.registerByElement(document.getElementById('contenteditable-control'))
|
||||
BesService.registerByElement(document.getElementById('readonly-control'))
|
||||
BesService.registerByElement(document.getElementById('textarea-control'), new BesStatusIconEventSink())
|
||||
BesService.registerByElement(document.getElementById('contenteditable-control'), new BesStatusIconEventSink())
|
||||
BesService.registerByElement(document.getElementById('readonly-control'), new BesStatusIconEventSink())
|
||||
if (my_ckeditor)
|
||||
BesCKService.register(my_ckeditor.ui.view.editable.element, my_ckeditor)
|
||||
BesCKService.register(my_ckeditor.ui.view.editable.element, my_ckeditor, new BesCKStatusIconEventSink())
|
||||
} else {
|
||||
BesService.unregisterByElement(document.getElementById('textarea-control'))
|
||||
BesService.unregisterByElement(document.getElementById('contenteditable-control'))
|
||||
|
Before Width: | Height: | Size: 979 B After Width: | Height: | Size: 979 B |
Before Width: | Height: | Size: 845 B After Width: | Height: | Size: 845 B |
Before Width: | Height: | Size: 776 B After Width: | Height: | Size: 776 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@ -20,3 +20,35 @@
|
||||
min-height: 100px;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.bes-status-div {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.bes-status-icon {
|
||||
position: relative;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-success {
|
||||
background-image: url('images/checkmark-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-loading {
|
||||
background-image: url('images/loading-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-error {
|
||||
background-image: url('images/error-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-mistakes {
|
||||
background-image: url('images/mistake-svgrepo-com.svg');
|
||||
}
|
||||
|
@ -8,11 +8,12 @@
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<script>const besUrl = 'http://localhost:225/api/v2';</script>
|
||||
<script src="../service.js"></script>
|
||||
<script src="common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p class="my-block">This is an example of a <code><textarea></code> edit control. Edit the text, resize the control or browser window, scroll around, click...</p>
|
||||
<div class="my-block">
|
||||
<textarea class="my-control bes-service">Tukaj vpišite besedilo ki ga želite popraviti.
|
||||
<textarea id="my-editor" class="my-control">Tukaj vpišite besedilo ki ga želite popraviti.
|
||||
|
||||
Prišla je njena lepa hčera. Smatram da tega nebi bilo potrebno storiti. Predavanje je trajalo dve ure. S njim grem v Kamnik. Janez jutri nebo prišel. Prišel je z 100 idejami.
|
||||
|
||||
@ -23,5 +24,8 @@ Na mizo nisem položil knjigo.
|
||||
Kvazimodo ji je ponavadi prinesel hrano in pijačo, medtem ko je spala, da ne bi videla njegov iznakažen in grd obraz. Poleg tega ji je pustil tudi piščalko, da bi ga lahko priklicala, če bi bilo to potrebno. Kvazimodo se je odločil, da razveseli Esmeraldo in ji obljubi, da ji bo pripeljal Febusa. Toda Febus ni želel priti. Kvazimodo ji je raje lagal, da ni mogel najti Febusa, kot da Esmeraldi pove resnico, ker bi ona trpela.</textarea>
|
||||
</div>
|
||||
<bes-popup-el/>
|
||||
<script>
|
||||
BesService.registerByElement(document.getElementById('my-editor'), new BesStatusIconEventSink())
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
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
|
||||
}
|
||||
|
32
styles.css
@ -39,38 +39,6 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bes-status-div {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.bes-status-icon {
|
||||
position: relative;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-success {
|
||||
background-image: url('images/checkmark-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-loading {
|
||||
background-image: url('images/loading-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-error {
|
||||
background-image: url('images/error-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-status-icon.bes-status-mistakes {
|
||||
background-image: url('images/mistake-svgrepo-com.svg');
|
||||
}
|
||||
|
||||
.bes-text-panel {
|
||||
position: absolute;
|
||||
color: transparent;
|
||||
|