overleaf/services/web/frontend/js/ide/SafariScrollPatcher.js
Alf Eaton 8b35db3fc1 Merge pull request #3928 from overleaf/ae-safari-error
Check for existence of elements before attaching event listeners in SafariScrollPatcher

GitOrigin-RevId: 67338581878871cf67fc050dc233da93f14753a7
2021-04-22 02:09:37 +00:00

117 lines
4.1 KiB
JavaScript

/* eslint-disable
max-len,
no-return-assign,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
let SafariScrollPatcher
export default SafariScrollPatcher = class SafariScrollPatcher {
constructor($scope) {
this.isOverAce = false // Flag to control if the pointer is over Ace.
this.pdfDiv = null
this.aceDiv = null
// Start listening to PDF wheel events when the pointer leaves the PDF region.
// P.S. This is the problem in a nutshell: although the pointer is elsewhere,
// wheel events keep being dispatched to the PDF.
this.handlePdfDivMouseLeave = () => {
return this.pdfDiv.addEventListener('wheel', this.dispatchToAce)
}
// Stop listening to wheel events when the pointer enters the PDF region. If
// the pointer is over the PDF, native behaviour is adequate.
this.handlePdfDivMouseEnter = () => {
return this.pdfDiv.removeEventListener('wheel', this.dispatchToAce)
}
// Set the "pointer over Ace" flag as false, when the mouse leaves its area.
this.handleAceDivMouseLeave = () => {
return (this.isOverAce = false)
}
// Set the "pointer over Ace" flag as true, when the mouse enters its area.
this.handleAceDivMouseEnter = () => {
return (this.isOverAce = true)
}
// Grab the elements (pdfDiv, aceDiv) and set the "hover" event listeners.
// If elements are already defined, clear existing event listeners and do
// the process again (grab elements, set listeners).
this.setListeners = () => {
this.isOverAce = false
// If elements aren't null, remove existing listeners.
if (this.pdfDiv != null) {
this.pdfDiv.removeEventListener(
'mouseleave',
this.handlePdfDivMouseLeave
)
this.pdfDiv.removeEventListener(
'mouseenter',
this.handlePdfDivMouseEnter
)
}
if (this.aceDiv != null) {
this.aceDiv.removeEventListener(
'mouseleave',
this.handleAceDivMouseLeave
)
this.aceDiv.removeEventListener(
'mouseenter',
this.handleAceDivMouseEnter
)
}
// Grab elements.
this.pdfDiv = document.querySelector('.pdfjs-viewer') // Grab the PDF div.
this.aceDiv = document.querySelector('.ace_content') // Also the editor.
// Set hover-related listeners.
if (this.pdfDiv != null) {
this.pdfDiv.addEventListener('mouseleave', this.handlePdfDivMouseLeave)
this.pdfDiv.addEventListener('mouseenter', this.handlePdfDivMouseEnter)
}
if (this.aceDiv != null) {
this.aceDiv.addEventListener('mouseleave', this.handleAceDivMouseLeave)
this.aceDiv.addEventListener('mouseenter', this.handleAceDivMouseEnter)
}
}
// Handler for wheel events on the PDF.
// If the pointer is over Ace, grab the event, prevent default behaviour
// and dispatch it to Ace.
this.dispatchToAce = e => {
if (this.isOverAce) {
// If this is logged, the problem just happened: the event arrived
// here (the PDF wheel handler), but it should've gone to Ace.
// Small timeout - if we dispatch immediately, an exception is thrown.
window.setTimeout(() => {
// Dispatch the exact same event to Ace (this will keep values
// values e.g. `wheelDelta` consistent with user interaction).
return this.aceDiv.dispatchEvent(e)
}, 1)
// Avoid scrolling the PDF, as we assume this was intended to the
// editor.
return e.preventDefault()
}
}
// "loaded" event is emitted from the pdfViewer controller $scope. This means
// that the previous PDF DOM element was destroyed and a new one is available,
// so we need to grab the elements and set the listeners again.
$scope.$on('loaded', () => {
return this.setListeners()
})
}
}