diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index 8c5b66ef3..b9fb74065 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -26,15 +26,11 @@ import { SimpleLinkService } from './pdf_link_service'; * @property {DownloadManager} downloadManager */ -/** - * @class - */ -var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() { +class AnnotationLayerBuilder { /** * @param {AnnotationLayerBuilderOptions} options - * @constructs AnnotationLayerBuilder */ - function AnnotationLayerBuilder(options) { + constructor(options) { this.pageDiv = options.pageDiv; this.pdfPage = options.pdfPage; this.renderInteractiveForms = options.renderInteractiveForms; @@ -44,88 +40,74 @@ var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() { this.div = null; } - AnnotationLayerBuilder.prototype = - /** @lends AnnotationLayerBuilder.prototype */ { - - /** - * @param {PageViewport} viewport - * @param {string} intent (default value is 'display') - */ - render: function AnnotationLayerBuilder_render(viewport, intent) { - var self = this; + /** + * @param {PageViewport} viewport + * @param {string} intent (default value is 'display') + */ + render(viewport, intent = 'display') { + this.pdfPage.getAnnotations({ intent }).then((annotations) => { var parameters = { - intent: (intent === undefined ? 'display' : intent), + viewport: viewport.clone({ dontFlip: true }), + div: this.div, + annotations, + page: this.pdfPage, + renderInteractiveForms: this.renderInteractiveForms, + linkService: this.linkService, + downloadManager: this.downloadManager, }; - this.pdfPage.getAnnotations(parameters).then(function (annotations) { - viewport = viewport.clone({ dontFlip: true }); - parameters = { - viewport: viewport, - div: self.div, - annotations: annotations, - page: self.pdfPage, - renderInteractiveForms: self.renderInteractiveForms, - linkService: self.linkService, - downloadManager: self.downloadManager, - }; - - if (self.div) { - // If an annotationLayer already exists, refresh its children's - // transformation matrices. - AnnotationLayer.update(parameters); - } else { - // Create an annotation layer div and render the annotations - // if there is at least one annotation. - if (annotations.length === 0) { - return; - } - - self.div = document.createElement('div'); - self.div.className = 'annotationLayer'; - self.pageDiv.appendChild(self.div); - parameters.div = self.div; - - AnnotationLayer.render(parameters); - if (typeof mozL10n !== 'undefined') { - mozL10n.translate(self.div); - } + if (this.div) { + // If an annotationLayer already exists, refresh its children's + // transformation matrices. + AnnotationLayer.update(parameters); + } else { + // Create an annotation layer div and render the annotations + // if there is at least one annotation. + if (annotations.length === 0) { + return; } - }); - }, - hide: function AnnotationLayerBuilder_hide() { - if (!this.div) { - return; + this.div = document.createElement('div'); + this.div.className = 'annotationLayer'; + this.pageDiv.appendChild(this.div); + parameters.div = this.div; + + AnnotationLayer.render(parameters); + if (typeof mozL10n !== 'undefined') { + mozL10n.translate(this.div); + } } - this.div.setAttribute('hidden', 'true'); - } - }; + }); + } - return AnnotationLayerBuilder; -})(); + hide() { + if (!this.div) { + return; + } + this.div.setAttribute('hidden', 'true'); + } +} /** - * @constructor * @implements IPDFAnnotationLayerFactory */ -function DefaultAnnotationLayerFactory() {} -DefaultAnnotationLayerFactory.prototype = { +class DefaultAnnotationLayerFactory { /** * @param {HTMLDivElement} pageDiv * @param {PDFPage} pdfPage * @param {boolean} renderInteractiveForms * @returns {AnnotationLayerBuilder} */ - createAnnotationLayerBuilder: function (pageDiv, pdfPage, - renderInteractiveForms) { + createAnnotationLayerBuilder(pageDiv, pdfPage, + renderInteractiveForms = false) { return new AnnotationLayerBuilder({ - pageDiv: pageDiv, - pdfPage: pdfPage, - renderInteractiveForms: renderInteractiveForms, + pageDiv, + pdfPage, + renderInteractiveForms, linkService: new SimpleLinkService(), }); } -}; +} export { AnnotationLayerBuilder, diff --git a/web/interfaces.js b/web/interfaces.js index 291cfc440..6c2b9a02a 100644 --- a/web/interfaces.js +++ b/web/interfaces.js @@ -108,14 +108,13 @@ IPDFTextLayerFactory.prototype = { /** * @interface */ -function IPDFAnnotationLayerFactory() {} -IPDFAnnotationLayerFactory.prototype = { +class IPDFAnnotationLayerFactory { // eslint-disable-line no-unused-vars /** * @param {HTMLDivElement} pageDiv * @param {PDFPage} pdfPage * @param {boolean} renderInteractiveForms * @returns {AnnotationLayerBuilder} */ - createAnnotationLayerBuilder: function (pageDiv, pdfPage, - renderInteractiveForms) {} -}; + createAnnotationLayerBuilder(pageDiv, pdfPage, + renderInteractiveForms = false) {} +} diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index c9f3d8066..9b29bd5d3 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -15,10 +15,19 @@ import { normalizeWheelEventDelta } from './ui_utils'; -var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms -var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms -var ACTIVE_SELECTOR = 'pdfPresentationMode'; -var CONTROLS_SELECTOR = 'pdfPresentationModeControls'; +const DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms +const DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms +const ACTIVE_SELECTOR = 'pdfPresentationMode'; +const CONTROLS_SELECTOR = 'pdfPresentationModeControls'; +const MOUSE_SCROLL_COOLDOWN_TIME = 50; // in ms +const PAGE_SWITCH_THRESHOLD = 0.1; + +// Number of CSS pixels for a movement to count as a swipe. +const SWIPE_MIN_DISTANCE_THRESHOLD = 50; + +// Swipe angle deviation from the x or y axis before it is not +// considered a swipe in that direction any more. +const SWIPE_ANGLE_THRESHOLD = Math.PI / 6; /** * @typedef {Object} PDFPresentationModeOptions @@ -30,15 +39,11 @@ var CONTROLS_SELECTOR = 'pdfPresentationModeControls'; * to the context menu in Presentation Mode. */ -/** - * @class - */ -var PDFPresentationMode = (function PDFPresentationModeClosure() { +class PDFPresentationMode { /** - * @constructs PDFPresentationMode * @param {PDFPresentationModeOptions} options */ - function PDFPresentationMode(options) { + constructor(options) { this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; this.pdfViewer = options.pdfViewer; @@ -53,457 +58,432 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { this.touchSwipeState = null; if (contextMenuItems) { - contextMenuItems.contextFirstPage.addEventListener('click', - function PDFPresentationMode_contextFirstPageClick(e) { + contextMenuItems.contextFirstPage.addEventListener('click', () => { this.contextMenuOpen = false; this.eventBus.dispatch('firstpage'); - }.bind(this)); - contextMenuItems.contextLastPage.addEventListener('click', - function PDFPresentationMode_contextLastPageClick(e) { + }); + contextMenuItems.contextLastPage.addEventListener('click', () => { this.contextMenuOpen = false; this.eventBus.dispatch('lastpage'); - }.bind(this)); - contextMenuItems.contextPageRotateCw.addEventListener('click', - function PDFPresentationMode_contextPageRotateCwClick(e) { + }); + contextMenuItems.contextPageRotateCw.addEventListener('click', () => { this.contextMenuOpen = false; this.eventBus.dispatch('rotatecw'); - }.bind(this)); - contextMenuItems.contextPageRotateCcw.addEventListener('click', - function PDFPresentationMode_contextPageRotateCcwClick(e) { + }); + contextMenuItems.contextPageRotateCcw.addEventListener('click', () => { this.contextMenuOpen = false; this.eventBus.dispatch('rotateccw'); - }.bind(this)); + }); } } - PDFPresentationMode.prototype = { - /** - * Request the browser to enter fullscreen mode. - * @returns {boolean} Indicating if the request was successful. - */ - request: function PDFPresentationMode_request() { - if (this.switchInProgress || this.active || - !this.viewer.hasChildNodes()) { - return false; - } - this._addFullscreenChangeListeners(); - this._setSwitchInProgress(); - this._notifyStateChange(); + /** + * Request the browser to enter fullscreen mode. + * @returns {boolean} Indicating if the request was successful. + */ + request() { + if (this.switchInProgress || this.active || !this.viewer.hasChildNodes()) { + return false; + } + this._addFullscreenChangeListeners(); + this._setSwitchInProgress(); + this._notifyStateChange(); - if (this.container.requestFullscreen) { - this.container.requestFullscreen(); - } else if (this.container.mozRequestFullScreen) { - this.container.mozRequestFullScreen(); - } else if (this.container.webkitRequestFullscreen) { - this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } else if (this.container.msRequestFullscreen) { - this.container.msRequestFullscreen(); - } else { - return false; - } + if (this.container.requestFullscreen) { + this.container.requestFullscreen(); + } else if (this.container.mozRequestFullScreen) { + this.container.mozRequestFullScreen(); + } else if (this.container.webkitRequestFullscreen) { + this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else if (this.container.msRequestFullscreen) { + this.container.msRequestFullscreen(); + } else { + return false; + } - this.args = { - page: this.pdfViewer.currentPageNumber, - previousScale: this.pdfViewer.currentScaleValue, - }; + this.args = { + page: this.pdfViewer.currentPageNumber, + previousScale: this.pdfViewer.currentScaleValue, + }; - return true; - }, + return true; + } - /** - * @private - */ - _mouseWheel: function PDFPresentationMode_mouseWheel(evt) { - if (!this.active) { - return; - } + /** + * @private + */ + _mouseWheel(evt) { + if (!this.active) { + return; + } - evt.preventDefault(); + evt.preventDefault(); - var delta = normalizeWheelEventDelta(evt); + var delta = normalizeWheelEventDelta(evt); + var currentTime = (new Date()).getTime(); + var storedTime = this.mouseScrollTimeStamp; - var MOUSE_SCROLL_COOLDOWN_TIME = 50; - var PAGE_SWITCH_THRESHOLD = 0.1; - - var currentTime = (new Date()).getTime(); - var storedTime = this.mouseScrollTimeStamp; - - // If we've already switched page, avoid accidentally switching again. - if (currentTime > storedTime && - currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { - return; - } - // If the scroll direction changed, reset the accumulated scroll delta. - if ((this.mouseScrollDelta > 0 && delta < 0) || - (this.mouseScrollDelta < 0 && delta > 0)) { - this._resetMouseScrollState(); - } - this.mouseScrollDelta += delta; - - if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { - var totalDelta = this.mouseScrollDelta; - this._resetMouseScrollState(); - var success = totalDelta > 0 ? this._goToPreviousPage() - : this._goToNextPage(); - if (success) { - this.mouseScrollTimeStamp = currentTime; - } - } - }, - - get isFullscreen() { - return !!(document.fullscreenElement || - document.mozFullScreen || - document.webkitIsFullScreen || - document.msFullscreenElement); - }, - - /** - * @private - */ - _goToPreviousPage: function PDFPresentationMode_goToPreviousPage() { - var page = this.pdfViewer.currentPageNumber; - // If we're at the first page, we don't need to do anything. - if (page <= 1) { - return false; - } - this.pdfViewer.currentPageNumber = (page - 1); - return true; - }, - - /** - * @private - */ - _goToNextPage: function PDFPresentationMode_goToNextPage() { - var page = this.pdfViewer.currentPageNumber; - // If we're at the last page, we don't need to do anything. - if (page >= this.pdfViewer.pagesCount) { - return false; - } - this.pdfViewer.currentPageNumber = (page + 1); - return true; - }, - - /** - * @private - */ - _notifyStateChange: function PDFPresentationMode_notifyStateChange() { - this.eventBus.dispatch('presentationmodechanged', { - source: this, - active: this.active, - switchInProgress: !!this.switchInProgress - }); - }, - - /** - * Used to initialize a timeout when requesting Presentation Mode, - * i.e. when the browser is requested to enter fullscreen mode. - * This timeout is used to prevent the current page from being scrolled - * partially, or completely, out of view when entering Presentation Mode. - * NOTE: This issue seems limited to certain zoom levels (e.g. page-width). - * @private - */ - _setSwitchInProgress: function PDFPresentationMode_setSwitchInProgress() { - if (this.switchInProgress) { - clearTimeout(this.switchInProgress); - } - this.switchInProgress = setTimeout(function switchInProgressTimeout() { - this._removeFullscreenChangeListeners(); - delete this.switchInProgress; - this._notifyStateChange(); - }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); - }, - - /** - * @private - */ - _resetSwitchInProgress: - function PDFPresentationMode_resetSwitchInProgress() { - if (this.switchInProgress) { - clearTimeout(this.switchInProgress); - delete this.switchInProgress; - } - }, - - /** - * @private - */ - _enter: function PDFPresentationMode_enter() { - this.active = true; - this._resetSwitchInProgress(); - this._notifyStateChange(); - this.container.classList.add(ACTIVE_SELECTOR); - - // Ensure that the correct page is scrolled into view when entering - // Presentation Mode, by waiting until fullscreen mode in enabled. - setTimeout(function enterPresentationModeTimeout() { - this.pdfViewer.currentPageNumber = this.args.page; - this.pdfViewer.currentScaleValue = 'page-fit'; - }.bind(this), 0); - - this._addWindowListeners(); - this._showControls(); - this.contextMenuOpen = false; - this.container.setAttribute('contextmenu', 'viewerContextMenu'); - - // Text selection is disabled in Presentation Mode, thus it's not possible - // for the user to deselect text that is selected (e.g. with "Select all") - // when entering Presentation Mode, hence we remove any active selection. - window.getSelection().removeAllRanges(); - }, - - /** - * @private - */ - _exit: function PDFPresentationMode_exit() { - var page = this.pdfViewer.currentPageNumber; - this.container.classList.remove(ACTIVE_SELECTOR); - - // Ensure that the correct page is scrolled into view when exiting - // Presentation Mode, by waiting until fullscreen mode is disabled. - setTimeout(function exitPresentationModeTimeout() { - this.active = false; - this._removeFullscreenChangeListeners(); - this._notifyStateChange(); - - this.pdfViewer.currentScaleValue = this.args.previousScale; - this.pdfViewer.currentPageNumber = page; - this.args = null; - }.bind(this), 0); - - this._removeWindowListeners(); - this._hideControls(); + // If we've already switched page, avoid accidentally switching again. + if (currentTime > storedTime && + currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + return; + } + // If the scroll direction changed, reset the accumulated scroll delta. + if ((this.mouseScrollDelta > 0 && delta < 0) || + (this.mouseScrollDelta < 0 && delta > 0)) { this._resetMouseScrollState(); - this.container.removeAttribute('contextmenu'); + } + this.mouseScrollDelta += delta; + + if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { + var totalDelta = this.mouseScrollDelta; + this._resetMouseScrollState(); + var success = totalDelta > 0 ? this._goToPreviousPage() + : this._goToNextPage(); + if (success) { + this.mouseScrollTimeStamp = currentTime; + } + } + } + + get isFullscreen() { + return !!(document.fullscreenElement || document.mozFullScreen || + document.webkitIsFullScreen || document.msFullscreenElement); + } + + /** + * @private + */ + _goToPreviousPage() { + var page = this.pdfViewer.currentPageNumber; + // If we're at the first page, we don't need to do anything. + if (page <= 1) { + return false; + } + this.pdfViewer.currentPageNumber = (page - 1); + return true; + } + + /** + * @private + */ + _goToNextPage() { + var page = this.pdfViewer.currentPageNumber; + // If we're at the last page, we don't need to do anything. + if (page >= this.pdfViewer.pagesCount) { + return false; + } + this.pdfViewer.currentPageNumber = (page + 1); + return true; + } + + /** + * @private + */ + _notifyStateChange() { + this.eventBus.dispatch('presentationmodechanged', { + source: this, + active: this.active, + switchInProgress: !!this.switchInProgress, + }); + } + + /** + * Used to initialize a timeout when requesting Presentation Mode, + * i.e. when the browser is requested to enter fullscreen mode. + * This timeout is used to prevent the current page from being scrolled + * partially, or completely, out of view when entering Presentation Mode. + * NOTE: This issue seems limited to certain zoom levels (e.g. page-width). + * + * @private + */ + _setSwitchInProgress() { + if (this.switchInProgress) { + clearTimeout(this.switchInProgress); + } + this.switchInProgress = setTimeout(() => { + this._removeFullscreenChangeListeners(); + delete this.switchInProgress; + this._notifyStateChange(); + }, DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); + } + + /** + * @private + */ + _resetSwitchInProgress() { + if (this.switchInProgress) { + clearTimeout(this.switchInProgress); + delete this.switchInProgress; + } + } + + /** + * @private + */ + _enter() { + this.active = true; + this._resetSwitchInProgress(); + this._notifyStateChange(); + this.container.classList.add(ACTIVE_SELECTOR); + + // Ensure that the correct page is scrolled into view when entering + // Presentation Mode, by waiting until fullscreen mode in enabled. + setTimeout(() => { + this.pdfViewer.currentPageNumber = this.args.page; + this.pdfViewer.currentScaleValue = 'page-fit'; + }, 0); + + this._addWindowListeners(); + this._showControls(); + this.contextMenuOpen = false; + this.container.setAttribute('contextmenu', 'viewerContextMenu'); + + // Text selection is disabled in Presentation Mode, thus it's not possible + // for the user to deselect text that is selected (e.g. with "Select all") + // when entering Presentation Mode, hence we remove any active selection. + window.getSelection().removeAllRanges(); + } + + /** + * @private + */ + _exit() { + var page = this.pdfViewer.currentPageNumber; + this.container.classList.remove(ACTIVE_SELECTOR); + + // Ensure that the correct page is scrolled into view when exiting + // Presentation Mode, by waiting until fullscreen mode is disabled. + setTimeout(() => { + this.active = false; + this._removeFullscreenChangeListeners(); + this._notifyStateChange(); + + this.pdfViewer.currentScaleValue = this.args.previousScale; + this.pdfViewer.currentPageNumber = page; + this.args = null; + }, 0); + + this._removeWindowListeners(); + this._hideControls(); + this._resetMouseScrollState(); + this.container.removeAttribute('contextmenu'); + this.contextMenuOpen = false; + } + + /** + * @private + */ + _mouseDown(evt) { + if (this.contextMenuOpen) { this.contextMenuOpen = false; - }, - - /** - * @private - */ - _mouseDown: function PDFPresentationMode_mouseDown(evt) { - if (this.contextMenuOpen) { - this.contextMenuOpen = false; + evt.preventDefault(); + return; + } + if (evt.button === 0) { + // Enable clicking of links in presentation mode. Note: only links + // pointing to destinations in the current PDF document work. + var isInternalLink = (evt.target.href && + evt.target.classList.contains('internalLink')); + if (!isInternalLink) { + // Unless an internal link was clicked, advance one page. evt.preventDefault(); - return; + this.pdfViewer.currentPageNumber += (evt.shiftKey ? -1 : 1); } - if (evt.button === 0) { - // Enable clicking of links in presentation mode. Please note: - // Only links pointing to destinations in the current PDF document work. - var isInternalLink = (evt.target.href && - evt.target.classList.contains('internalLink')); - if (!isInternalLink) { - // Unless an internal link was clicked, advance one page. - evt.preventDefault(); - this.pdfViewer.currentPageNumber += (evt.shiftKey ? -1 : 1); - } - } - }, + } + } - /** - * @private - */ - _contextMenu: function PDFPresentationMode_contextMenu() { - this.contextMenuOpen = true; - }, + /** + * @private + */ + _contextMenu() { + this.contextMenuOpen = true; + } - /** - * @private - */ - _showControls: function PDFPresentationMode_showControls() { - if (this.controlsTimeout) { - clearTimeout(this.controlsTimeout); - } else { - this.container.classList.add(CONTROLS_SELECTOR); - } - this.controlsTimeout = setTimeout(function showControlsTimeout() { - this.container.classList.remove(CONTROLS_SELECTOR); - delete this.controlsTimeout; - }.bind(this), DELAY_BEFORE_HIDING_CONTROLS); - }, - - /** - * @private - */ - _hideControls: function PDFPresentationMode_hideControls() { - if (!this.controlsTimeout) { - return; - } + /** + * @private + */ + _showControls() { + if (this.controlsTimeout) { clearTimeout(this.controlsTimeout); + } else { + this.container.classList.add(CONTROLS_SELECTOR); + } + this.controlsTimeout = setTimeout(() => { this.container.classList.remove(CONTROLS_SELECTOR); delete this.controlsTimeout; - }, + }, DELAY_BEFORE_HIDING_CONTROLS); + } - /** - * Resets the properties used for tracking mouse scrolling events. - * @private - */ - _resetMouseScrollState: - function PDFPresentationMode_resetMouseScrollState() { - this.mouseScrollTimeStamp = 0; - this.mouseScrollDelta = 0; - }, - - /** - * @private - */ - _touchSwipe: function PDFPresentationMode_touchSwipe(evt) { - if (!this.active) { - return; - } - - // Must move at least these many CSS pixels for it to count as a swipe - var SWIPE_MIN_DISTANCE_THRESHOLD = 50; - // The swipe angle is allowed to deviate from the x or y axis by this much - // before it is not considered a swipe in that direction any more. - var SWIPE_ANGLE_THRESHOLD = Math.PI / 6; - - if (evt.touches.length > 1) { - // Multiple touch points detected, cancel the swipe. - this.touchSwipeState = null; - return; - } - switch (evt.type) { - case 'touchstart': - this.touchSwipeState = { - startX: evt.touches[0].pageX, - startY: evt.touches[0].pageY, - endX: evt.touches[0].pageX, - endY: evt.touches[0].pageY - }; - break; - case 'touchmove': - if (this.touchSwipeState === null) { - return; - } - this.touchSwipeState.endX = evt.touches[0].pageX; - this.touchSwipeState.endY = evt.touches[0].pageY; - // Do a preventDefault to avoid the swipe from triggering browser - // gestures (Chrome in particular has some sort of swipe gesture in - // fullscreen mode). - evt.preventDefault(); - break; - case 'touchend': - if (this.touchSwipeState === null) { - return; - } - var delta = 0; - var dx = this.touchSwipeState.endX - this.touchSwipeState.startX; - var dy = this.touchSwipeState.endY - this.touchSwipeState.startY; - var absAngle = Math.abs(Math.atan2(dy, dx)); - if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && - (absAngle <= SWIPE_ANGLE_THRESHOLD || - absAngle >= (Math.PI - SWIPE_ANGLE_THRESHOLD))) { - // horizontal swipe - delta = dx; - } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && - Math.abs(absAngle - (Math.PI / 2)) <= SWIPE_ANGLE_THRESHOLD) { - // vertical swipe - delta = dy; - } - if (delta > 0) { - this._goToPreviousPage(); - } else if (delta < 0) { - this._goToNextPage(); - } - break; - } - }, - - /** - * @private - */ - _addWindowListeners: function PDFPresentationMode_addWindowListeners() { - this.showControlsBind = this._showControls.bind(this); - this.mouseDownBind = this._mouseDown.bind(this); - this.mouseWheelBind = this._mouseWheel.bind(this); - this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this); - this.contextMenuBind = this._contextMenu.bind(this); - this.touchSwipeBind = this._touchSwipe.bind(this); - - window.addEventListener('mousemove', this.showControlsBind); - window.addEventListener('mousedown', this.mouseDownBind); - window.addEventListener('wheel', this.mouseWheelBind); - window.addEventListener('keydown', this.resetMouseScrollStateBind); - window.addEventListener('contextmenu', this.contextMenuBind); - window.addEventListener('touchstart', this.touchSwipeBind); - window.addEventListener('touchmove', this.touchSwipeBind); - window.addEventListener('touchend', this.touchSwipeBind); - }, - - /** - * @private - */ - _removeWindowListeners: - function PDFPresentationMode_removeWindowListeners() { - window.removeEventListener('mousemove', this.showControlsBind); - window.removeEventListener('mousedown', this.mouseDownBind); - window.removeEventListener('wheel', this.mouseWheelBind); - window.removeEventListener('keydown', this.resetMouseScrollStateBind); - window.removeEventListener('contextmenu', this.contextMenuBind); - window.removeEventListener('touchstart', this.touchSwipeBind); - window.removeEventListener('touchmove', this.touchSwipeBind); - window.removeEventListener('touchend', this.touchSwipeBind); - - delete this.showControlsBind; - delete this.mouseDownBind; - delete this.mouseWheelBind; - delete this.resetMouseScrollStateBind; - delete this.contextMenuBind; - delete this.touchSwipeBind; - }, - - /** - * @private - */ - _fullscreenChange: function PDFPresentationMode_fullscreenChange() { - if (this.isFullscreen) { - this._enter(); - } else { - this._exit(); - } - }, - - /** - * @private - */ - _addFullscreenChangeListeners: - function PDFPresentationMode_addFullscreenChangeListeners() { - this.fullscreenChangeBind = this._fullscreenChange.bind(this); - - window.addEventListener('fullscreenchange', this.fullscreenChangeBind); - window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind); - if (typeof PDFJSDev === 'undefined' || - !PDFJSDev.test('FIREFOX || MOZCENTRAL')) { - window.addEventListener('webkitfullscreenchange', - this.fullscreenChangeBind); - window.addEventListener('MSFullscreenChange', - this.fullscreenChangeBind); - } - }, - - /** - * @private - */ - _removeFullscreenChangeListeners: - function PDFPresentationMode_removeFullscreenChangeListeners() { - window.removeEventListener('fullscreenchange', this.fullscreenChangeBind); - window.removeEventListener('mozfullscreenchange', - this.fullscreenChangeBind); - if (typeof PDFJSDev === 'undefined' || - !PDFJSDev.test('FIREFOX || MOZCENTRAL')) { - window.removeEventListener('webkitfullscreenchange', - this.fullscreenChangeBind); - window.removeEventListener('MSFullscreenChange', - this.fullscreenChangeBind); - } - - delete this.fullscreenChangeBind; + /** + * @private + */ + _hideControls() { + if (!this.controlsTimeout) { + return; } - }; + clearTimeout(this.controlsTimeout); + this.container.classList.remove(CONTROLS_SELECTOR); + delete this.controlsTimeout; + } - return PDFPresentationMode; -})(); + /** + * Resets the properties used for tracking mouse scrolling events. + * + * @private + */ + _resetMouseScrollState() { + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; + } + + /** + * @private + */ + _touchSwipe(evt) { + if (!this.active) { + return; + } + if (evt.touches.length > 1) { + // Multiple touch points detected; cancel the swipe. + this.touchSwipeState = null; + return; + } + + switch (evt.type) { + case 'touchstart': + this.touchSwipeState = { + startX: evt.touches[0].pageX, + startY: evt.touches[0].pageY, + endX: evt.touches[0].pageX, + endY: evt.touches[0].pageY, + }; + break; + case 'touchmove': + if (this.touchSwipeState === null) { + return; + } + this.touchSwipeState.endX = evt.touches[0].pageX; + this.touchSwipeState.endY = evt.touches[0].pageY; + // Avoid the swipe from triggering browser gestures (Chrome in + // particular has some sort of swipe gesture in fullscreen mode). + evt.preventDefault(); + break; + case 'touchend': + if (this.touchSwipeState === null) { + return; + } + var delta = 0; + var dx = this.touchSwipeState.endX - this.touchSwipeState.startX; + var dy = this.touchSwipeState.endY - this.touchSwipeState.startY; + var absAngle = Math.abs(Math.atan2(dy, dx)); + if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && + (absAngle <= SWIPE_ANGLE_THRESHOLD || + absAngle >= (Math.PI - SWIPE_ANGLE_THRESHOLD))) { + // Horizontal swipe. + delta = dx; + } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && + Math.abs(absAngle - (Math.PI / 2)) <= SWIPE_ANGLE_THRESHOLD) { + // Vertical swipe. + delta = dy; + } + if (delta > 0) { + this._goToPreviousPage(); + } else if (delta < 0) { + this._goToNextPage(); + } + break; + } + } + + /** + * @private + */ + _addWindowListeners() { + this.showControlsBind = this._showControls.bind(this); + this.mouseDownBind = this._mouseDown.bind(this); + this.mouseWheelBind = this._mouseWheel.bind(this); + this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this); + this.contextMenuBind = this._contextMenu.bind(this); + this.touchSwipeBind = this._touchSwipe.bind(this); + + window.addEventListener('mousemove', this.showControlsBind); + window.addEventListener('mousedown', this.mouseDownBind); + window.addEventListener('wheel', this.mouseWheelBind); + window.addEventListener('keydown', this.resetMouseScrollStateBind); + window.addEventListener('contextmenu', this.contextMenuBind); + window.addEventListener('touchstart', this.touchSwipeBind); + window.addEventListener('touchmove', this.touchSwipeBind); + window.addEventListener('touchend', this.touchSwipeBind); + } + + /** + * @private + */ + _removeWindowListeners() { + window.removeEventListener('mousemove', this.showControlsBind); + window.removeEventListener('mousedown', this.mouseDownBind); + window.removeEventListener('wheel', this.mouseWheelBind); + window.removeEventListener('keydown', this.resetMouseScrollStateBind); + window.removeEventListener('contextmenu', this.contextMenuBind); + window.removeEventListener('touchstart', this.touchSwipeBind); + window.removeEventListener('touchmove', this.touchSwipeBind); + window.removeEventListener('touchend', this.touchSwipeBind); + + delete this.showControlsBind; + delete this.mouseDownBind; + delete this.mouseWheelBind; + delete this.resetMouseScrollStateBind; + delete this.contextMenuBind; + delete this.touchSwipeBind; + } + + /** + * @private + */ + _fullscreenChange() { + if (this.isFullscreen) { + this._enter(); + } else { + this._exit(); + } + } + + /** + * @private + */ + _addFullscreenChangeListeners() { + this.fullscreenChangeBind = this._fullscreenChange.bind(this); + + window.addEventListener('fullscreenchange', this.fullscreenChangeBind); + window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind); + if (typeof PDFJSDev === 'undefined' || + !PDFJSDev.test('FIREFOX || MOZCENTRAL')) { + window.addEventListener('webkitfullscreenchange', + this.fullscreenChangeBind); + window.addEventListener('MSFullscreenChange', + this.fullscreenChangeBind); + } + } + + /** + * @private + */ + _removeFullscreenChangeListeners() { + window.removeEventListener('fullscreenchange', this.fullscreenChangeBind); + window.removeEventListener('mozfullscreenchange', + this.fullscreenChangeBind); + if (typeof PDFJSDev === 'undefined' || + !PDFJSDev.test('FIREFOX || MOZCENTRAL')) { + window.removeEventListener('webkitfullscreenchange', + this.fullscreenChangeBind); + window.removeEventListener('MSFullscreenChange', + this.fullscreenChangeBind); + } + + delete this.fullscreenChangeBind; + } +} export { PDFPresentationMode, diff --git a/web/pdf_rendering_queue.js b/web/pdf_rendering_queue.js index 08666eebf..e3a9e2fc9 100644 --- a/web/pdf_rendering_queue.js +++ b/web/pdf_rendering_queue.js @@ -13,166 +13,164 @@ * limitations under the License. */ -var CLEANUP_TIMEOUT = 30000; +const CLEANUP_TIMEOUT = 30000; -var RenderingStates = { +const RenderingStates = { INITIAL: 0, RUNNING: 1, PAUSED: 2, - FINISHED: 3 + FINISHED: 3, }; /** * Controls rendering of the views for pages and thumbnails. - * @class */ -var PDFRenderingQueue = (function PDFRenderingQueueClosure() { - /** - * @constructs - */ - function PDFRenderingQueue() { +class PDFRenderingQueue { + constructor() { this.pdfViewer = null; this.pdfThumbnailViewer = null; this.onIdle = null; - this.highestPriorityPage = null; this.idleTimeout = null; this.printing = false; this.isThumbnailViewEnabled = false; } - PDFRenderingQueue.prototype = /** @lends PDFRenderingQueue.prototype */ { - /** - * @param {PDFViewer} pdfViewer - */ - setViewer: function PDFRenderingQueue_setViewer(pdfViewer) { - this.pdfViewer = pdfViewer; - }, + /** + * @param {PDFViewer} pdfViewer + */ + setViewer(pdfViewer) { + this.pdfViewer = pdfViewer; + } - /** - * @param {PDFThumbnailViewer} pdfThumbnailViewer - */ - setThumbnailViewer: - function PDFRenderingQueue_setThumbnailViewer(pdfThumbnailViewer) { - this.pdfThumbnailViewer = pdfThumbnailViewer; - }, + /** + * @param {PDFThumbnailViewer} pdfThumbnailViewer + */ + setThumbnailViewer(pdfThumbnailViewer) { + this.pdfThumbnailViewer = pdfThumbnailViewer; + } - /** - * @param {IRenderableView} view - * @returns {boolean} - */ - isHighestPriority: function PDFRenderingQueue_isHighestPriority(view) { - return this.highestPriorityPage === view.renderingId; - }, + /** + * @param {IRenderableView} view + * @returns {boolean} + */ + isHighestPriority(view) { + return this.highestPriorityPage === view.renderingId; + } - renderHighestPriority: function - PDFRenderingQueue_renderHighestPriority(currentlyVisiblePages) { - if (this.idleTimeout) { - clearTimeout(this.idleTimeout); - this.idleTimeout = null; - } + /** + * @param {Object} currentlyVisiblePages + */ + renderHighestPriority(currentlyVisiblePages) { + if (this.idleTimeout) { + clearTimeout(this.idleTimeout); + this.idleTimeout = null; + } - // Pages have a higher priority than thumbnails, so check them first. - if (this.pdfViewer.forceRendering(currentlyVisiblePages)) { + // Pages have a higher priority than thumbnails, so check them first. + if (this.pdfViewer.forceRendering(currentlyVisiblePages)) { + return; + } + // No pages needed rendering, so check thumbnails. + if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) { + if (this.pdfThumbnailViewer.forceRendering()) { return; } - // No pages needed rendering so check thumbnails. - if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) { - if (this.pdfThumbnailViewer.forceRendering()) { - return; - } - } + } - if (this.printing) { - // If printing is currently ongoing do not reschedule cleanup. - return; - } + if (this.printing) { + // If printing is currently ongoing do not reschedule cleanup. + return; + } - if (this.onIdle) { - this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT); - } - }, + if (this.onIdle) { + this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT); + } + } - getHighestPriority: function - PDFRenderingQueue_getHighestPriority(visible, views, scrolledDown) { - // The state has changed figure out which page has the highest priority to - // render next (if any). - // Priority: - // 1 visible pages - // 2 if last scrolled down page after the visible pages - // 2 if last scrolled up page before the visible pages - var visibleViews = visible.views; + /** + * @param {Object} visible + * @param {Array} views + * @param {boolean} scrolledDown + */ + getHighestPriority(visible, views, scrolledDown) { + /** + * The state has changed. Figure out which page has the highest priority to + * render next (if any). + * + * Priority: + * 1. visible pages + * 2. if last scrolled down, the page after the visible pages, or + * if last scrolled up, the page before the visible pages + */ + var visibleViews = visible.views; - var numVisible = visibleViews.length; - if (numVisible === 0) { - return false; - } - for (var i = 0; i < numVisible; ++i) { - var view = visibleViews[i].view; - if (!this.isViewFinished(view)) { - return view; - } + var numVisible = visibleViews.length; + if (numVisible === 0) { + return false; + } + for (var i = 0; i < numVisible; ++i) { + var view = visibleViews[i].view; + if (!this.isViewFinished(view)) { + return view; } + } - // All the visible views have rendered, try to render next/previous pages. - if (scrolledDown) { - var nextPageIndex = visible.last.id; - // ID's start at 1 so no need to add 1. - if (views[nextPageIndex] && - !this.isViewFinished(views[nextPageIndex])) { - return views[nextPageIndex]; - } - } else { - var previousPageIndex = visible.first.id - 2; - if (views[previousPageIndex] && + // All the visible views have rendered; try to render next/previous pages. + if (scrolledDown) { + var nextPageIndex = visible.last.id; + // IDs start at 1, so no need to add 1. + if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) { + return views[nextPageIndex]; + } + } else { + var previousPageIndex = visible.first.id - 2; + if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) { - return views[previousPageIndex]; - } + return views[previousPageIndex]; } - // Everything that needs to be rendered has been. - return null; - }, + } + // Everything that needs to be rendered has been. + return null; + } - /** - * @param {IRenderableView} view - * @returns {boolean} - */ - isViewFinished: function PDFRenderingQueue_isViewFinished(view) { - return view.renderingState === RenderingStates.FINISHED; - }, + /** + * @param {IRenderableView} view + * @returns {boolean} + */ + isViewFinished(view) { + return view.renderingState === RenderingStates.FINISHED; + } - /** - * Render a page or thumbnail view. This calls the appropriate function - * based on the views state. If the view is already rendered it will return - * false. - * @param {IRenderableView} view - */ - renderView: function PDFRenderingQueue_renderView(view) { - var state = view.renderingState; - switch (state) { - case RenderingStates.FINISHED: - return false; - case RenderingStates.PAUSED: - this.highestPriorityPage = view.renderingId; - view.resume(); - break; - case RenderingStates.RUNNING: - this.highestPriorityPage = view.renderingId; - break; - case RenderingStates.INITIAL: - this.highestPriorityPage = view.renderingId; - var continueRendering = function () { - this.renderHighestPriority(); - }.bind(this); - view.draw().then(continueRendering, continueRendering); - break; - } - return true; - }, - }; - - return PDFRenderingQueue; -})(); + /** + * Render a page or thumbnail view. This calls the appropriate function + * based on the views state. If the view is already rendered it will return + * `false`. + * + * @param {IRenderableView} view + */ + renderView(view) { + switch (view.renderingState) { + case RenderingStates.FINISHED: + return false; + case RenderingStates.PAUSED: + this.highestPriorityPage = view.renderingId; + view.resume(); + break; + case RenderingStates.RUNNING: + this.highestPriorityPage = view.renderingId; + break; + case RenderingStates.INITIAL: + this.highestPriorityPage = view.renderingId; + var continueRendering = () => { + this.renderHighestPriority(); + }; + view.draw().then(continueRendering, continueRendering); + break; + } + return true; + } +} export { RenderingStates,