From 9a437a158f754df875819e55ab689d2bb33598f9 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 26 Feb 2020 17:16:30 +0100 Subject: [PATCH 1/2] [api-minor] Deprecate `getGlobalEventBus` and update the "viewer components" examples accordingly To avoid outright breaking third-party usages of the "viewer components" the `getGlobalEventBus` functionality is left intact, but a deprecation message is printed if the function is invoked. The various examples are updated to *explicitly* initialize an `EventBus` instance, and provide that when initializing the relevant viewer components. --- examples/acroforms/acroforms.js | 3 +++ examples/components/pageviewer.js | 3 +++ examples/components/simpleviewer.js | 10 ++++++++-- examples/components/singlepageviewer.js | 10 ++++++++-- examples/mobile-viewer/viewer.js | 16 ++++++++++++---- examples/svgviewer/viewer.js | 9 +++++++-- web/app.js | 4 ++-- web/base_viewer.js | 7 +++++-- web/interfaces.js | 4 +++- web/pdf_find_bar.js | 4 ++-- web/pdf_find_controller.js | 4 ++-- web/pdf_page_view.js | 3 ++- web/pdf_viewer.component.js | 10 +--------- web/text_layer_builder.js | 5 ++++- web/ui_utils.js | 8 +++++--- web/viewer.js | 2 +- 16 files changed, 68 insertions(+), 34 deletions(-) diff --git a/examples/acroforms/acroforms.js b/examples/acroforms/acroforms.js index 75a0c8650..bcf4dae3f 100644 --- a/examples/acroforms/acroforms.js +++ b/examples/acroforms/acroforms.js @@ -23,6 +23,8 @@ var DEFAULT_SCALE = 1.0; var container = document.getElementById("pageContainer"); +var eventBus = new pdfjsViewer.EventBus(); + // Fetch the PDF document from the URL using promises. var loadingTask = pdfjsLib.getDocument(DEFAULT_URL); loadingTask.promise.then(function(doc) { @@ -39,6 +41,7 @@ loadingTask.promise.then(function(doc) { id: pageNum, scale: DEFAULT_SCALE, defaultViewport: pdfPage.getViewport({ scale: DEFAULT_SCALE }), + eventBus: eventBus, annotationLayerFactory: new pdfjsViewer.DefaultAnnotationLayerFactory(), renderInteractiveForms: true, }); diff --git a/examples/components/pageviewer.js b/examples/components/pageviewer.js index b6ed43ff9..fd8297990 100644 --- a/examples/components/pageviewer.js +++ b/examples/components/pageviewer.js @@ -35,6 +35,8 @@ var SCALE = 1.0; var container = document.getElementById("pageContainer"); +var eventBus = new pdfjsViewer.EventBus(); + // Loading document. var loadingTask = pdfjsLib.getDocument({ url: DEFAULT_URL, @@ -50,6 +52,7 @@ loadingTask.promise.then(function(pdfDocument) { id: PAGE_TO_VIEW, scale: SCALE, defaultViewport: pdfPage.getViewport({ scale: SCALE }), + eventBus: eventBus, // We can enable text/annotations layers, if needed textLayerFactory: new pdfjsViewer.DefaultTextLayerFactory(), annotationLayerFactory: new pdfjsViewer.DefaultAnnotationLayerFactory(), diff --git a/examples/components/simpleviewer.js b/examples/components/simpleviewer.js index d71820622..290a4f8cb 100644 --- a/examples/components/simpleviewer.js +++ b/examples/components/simpleviewer.js @@ -34,22 +34,28 @@ var SEARCH_FOR = ""; // try 'Mozilla'; var container = document.getElementById("viewerContainer"); +var eventBus = new pdfjsViewer.EventBus(); + // (Optionally) enable hyperlinks within PDF files. -var pdfLinkService = new pdfjsViewer.PDFLinkService(); +var pdfLinkService = new pdfjsViewer.PDFLinkService({ + eventBus: eventBus, +}); // (Optionally) enable find controller. var pdfFindController = new pdfjsViewer.PDFFindController({ + eventBus: eventBus, linkService: pdfLinkService, }); var pdfViewer = new pdfjsViewer.PDFViewer({ container: container, + eventBus: eventBus, linkService: pdfLinkService, findController: pdfFindController, }); pdfLinkService.setViewer(pdfViewer); -document.addEventListener("pagesinit", function() { +eventBus.on("pagesinit", function() { // We can use pdfViewer now, e.g. let's change default scale. pdfViewer.currentScaleValue = "page-width"; diff --git a/examples/components/singlepageviewer.js b/examples/components/singlepageviewer.js index 343052985..ff6460553 100644 --- a/examples/components/singlepageviewer.js +++ b/examples/components/singlepageviewer.js @@ -34,22 +34,28 @@ var SEARCH_FOR = ""; // try 'Mozilla'; var container = document.getElementById("viewerContainer"); +var eventBus = new pdfjsViewer.EventBus(); + // (Optionally) enable hyperlinks within PDF files. -var pdfLinkService = new pdfjsViewer.PDFLinkService(); +var pdfLinkService = new pdfjsViewer.PDFLinkService({ + eventBus: eventBus, +}); // (Optionally) enable find controller. var pdfFindController = new pdfjsViewer.PDFFindController({ + eventBus: eventBus, linkService: pdfLinkService, }); var pdfSinglePageViewer = new pdfjsViewer.PDFSinglePageViewer({ container: container, + eventBus: eventBus, linkService: pdfLinkService, findController: pdfFindController, }); pdfLinkService.setViewer(pdfSinglePageViewer); -document.addEventListener("pagesinit", function() { +eventBus.on("pagesinit", function() { // We can use pdfSinglePageViewer now, e.g. let's change default scale. pdfSinglePageViewer.currentScaleValue = "page-width"; diff --git a/examples/mobile-viewer/viewer.js b/examples/mobile-viewer/viewer.js index e875f72b8..befe718f6 100644 --- a/examples/mobile-viewer/viewer.js +++ b/examples/mobile-viewer/viewer.js @@ -40,6 +40,7 @@ var PDFViewerApplication = { pdfViewer: null, pdfHistory: null, pdfLinkService: null, + eventBus: null, /** * Opens PDF document specified by URL. @@ -340,7 +341,12 @@ var PDFViewerApplication = { }, initUI: function pdfViewInitUI() { - var linkService = new pdfjsViewer.PDFLinkService(); + var eventBus = new pdfjsViewer.EventBus(); + this.eventBus = eventBus; + + var linkService = new pdfjsViewer.PDFLinkService({ + eventBus: eventBus, + }); this.pdfLinkService = linkService; this.l10n = pdfjsViewer.NullL10n; @@ -348,6 +354,7 @@ var PDFViewerApplication = { var container = document.getElementById("viewerContainer"); var pdfViewer = new pdfjsViewer.PDFViewer({ container: container, + eventBus: eventBus, linkService: linkService, l10n: this.l10n, useOnlyCssZoom: USE_ONLY_CSS_ZOOM, @@ -357,6 +364,7 @@ var PDFViewerApplication = { linkService.setViewer(pdfViewer); this.pdfHistory = new pdfjsViewer.PDFHistory({ + eventBus: eventBus, linkService: linkService, }); linkService.setHistory(this.pdfHistory); @@ -394,15 +402,15 @@ var PDFViewerApplication = { } }); - document.addEventListener("pagesinit", function() { + eventBus.on("pagesinit", function() { // We can use pdfViewer now, e.g. let's change default scale. pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE; }); - document.addEventListener( + eventBus.on( "pagechanging", function(evt) { - var page = evt.detail.pageNumber; + var page = evt.pageNumber; var numPages = PDFViewerApplication.pagesCount; document.getElementById("pageNumber").value = page; diff --git a/examples/svgviewer/viewer.js b/examples/svgviewer/viewer.js index 03f837e45..b7dec1e28 100644 --- a/examples/svgviewer/viewer.js +++ b/examples/svgviewer/viewer.js @@ -33,18 +33,23 @@ var DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf"; var container = document.getElementById("viewerContainer"); +var eventBus = new pdfjsViewer.EventBus(); + // (Optionally) enable hyperlinks within PDF files. -var pdfLinkService = new pdfjsViewer.PDFLinkService(); +var pdfLinkService = new pdfjsViewer.PDFLinkService({ + eventBus: eventBus, +}); var pdfViewer = new pdfjsViewer.PDFViewer({ container: container, + eventBus: eventBus, linkService: pdfLinkService, renderer: "svg", textLayerMode: 0, }); pdfLinkService.setViewer(pdfViewer); -document.addEventListener("pagesinit", function() { +eventBus.on("pagesinit", function() { // We can use pdfViewer now, e.g. let's change default scale. pdfViewer.currentScaleValue = "page-width"; }); diff --git a/web/app.js b/web/app.js index 1610f6249..0441e8f21 100644 --- a/web/app.js +++ b/web/app.js @@ -18,7 +18,7 @@ import { animationStarted, AutoPrintRegExp, DEFAULT_SCALE_VALUE, - getGlobalEventBus, + EventBus, getPDFFileNameFromURL, isValidRotation, isValidScrollMode, @@ -343,7 +343,7 @@ const PDFViewerApplication = { const eventBus = appConfig.eventBus || - getGlobalEventBus(AppOptions.get("eventBusDispatchToDOM")); + new EventBus({ dispatchToDOM: AppOptions.get("eventBusDispatchToDOM") }); this.eventBus = eventBus; const pdfRenderingQueue = new PDFRenderingQueue(); diff --git a/web/base_viewer.js b/web/base_viewer.js index 78ab1fad9..77146eb9b 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -1088,17 +1088,20 @@ class BaseViewer { * @param {HTMLDivElement} textLayerDiv * @param {number} pageIndex * @param {PageViewport} viewport + * @param {boolean} enhanceTextSelection + * @param {EventBus} eventBus * @returns {TextLayerBuilder} */ createTextLayerBuilder( textLayerDiv, pageIndex, viewport, - enhanceTextSelection = false + enhanceTextSelection = false, + eventBus ) { return new TextLayerBuilder({ textLayerDiv, - eventBus: this.eventBus, + eventBus, pageIndex, viewport, findController: this.isInPresentationMode ? null : this.findController, diff --git a/web/interfaces.js b/web/interfaces.js index 6f38130c8..e254df5f5 100644 --- a/web/interfaces.js +++ b/web/interfaces.js @@ -146,13 +146,15 @@ class IPDFTextLayerFactory { * @param {number} pageIndex * @param {PageViewport} viewport * @param {boolean} enhanceTextSelection + * @param {EventBus} eventBus * @returns {TextLayerBuilder} */ createTextLayerBuilder( textLayerDiv, pageIndex, viewport, - enhanceTextSelection = false + enhanceTextSelection = false, + eventBus ) {} } diff --git a/web/pdf_find_bar.js b/web/pdf_find_bar.js index 00811b268..5ace2fc74 100644 --- a/web/pdf_find_bar.js +++ b/web/pdf_find_bar.js @@ -25,7 +25,7 @@ const MATCHES_COUNT_LIMIT = 1000; * is done by PDFFindController. */ class PDFFindBar { - constructor(options, eventBus = getGlobalEventBus(), l10n = NullL10n) { + constructor(options, eventBus, l10n = NullL10n) { this.opened = false; this.bar = options.bar || null; @@ -38,7 +38,7 @@ class PDFFindBar { this.findResultsCount = options.findResultsCount || null; this.findPreviousButton = options.findPreviousButton || null; this.findNextButton = options.findNextButton || null; - this.eventBus = eventBus; + this.eventBus = eventBus || getGlobalEventBus(); this.l10n = l10n; // Add event listeners to the DOM elements. diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index e92accdeb..53a560260 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -67,9 +67,9 @@ class PDFFindController { /** * @param {PDFFindControllerOptions} options */ - constructor({ linkService, eventBus = getGlobalEventBus() }) { + constructor({ linkService, eventBus }) { this._linkService = linkService; - this._eventBus = eventBus; + this._eventBus = eventBus || getGlobalEventBus(); this._reset(); eventBus.on("findbarclose", this._onFindBarClose.bind(this)); diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 7dd696f93..b81456880 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -444,7 +444,8 @@ class PDFPageView { textLayerDiv, this.id - 1, this.viewport, - this.textLayerMode === TextLayerMode.ENABLE_ENHANCE + this.textLayerMode === TextLayerMode.ENABLE_ENHANCE, + this.eventBus ); } this.textLayer = textLayer; diff --git a/web/pdf_viewer.component.js b/web/pdf_viewer.component.js index 513efaaf5..a73d85fdb 100644 --- a/web/pdf_viewer.component.js +++ b/web/pdf_viewer.component.js @@ -21,12 +21,7 @@ import { DefaultTextLayerFactory, TextLayerBuilder, } from "./text_layer_builder.js"; -import { - EventBus, - getGlobalEventBus, - NullL10n, - ProgressBar, -} from "./ui_utils.js"; +import { EventBus, NullL10n, ProgressBar } from "./ui_utils.js"; import { PDFLinkService, SimpleLinkService } from "./pdf_link_service.js"; import { DownloadManager } from "./download_manager.js"; import { GenericL10n } from "./genericl10n.js"; @@ -41,9 +36,6 @@ const pdfjsVersion = PDFJSDev.eval("BUNDLE_VERSION"); // eslint-disable-next-line no-unused-vars const pdfjsBuild = PDFJSDev.eval("BUNDLE_BUILD"); -// For backwards compatibility, ensure that events are re-dispatched to the DOM. -getGlobalEventBus(/* dispatchToDOM = */ true); - export { PDFViewer, PDFSinglePageViewer, diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index a7b5fb0e2..ec56a376c 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -444,19 +444,22 @@ class DefaultTextLayerFactory { * @param {number} pageIndex * @param {PageViewport} viewport * @param {boolean} enhanceTextSelection + * @param {EventBus} eventBus * @returns {TextLayerBuilder} */ createTextLayerBuilder( textLayerDiv, pageIndex, viewport, - enhanceTextSelection = false + enhanceTextSelection = false, + eventBus ) { return new TextLayerBuilder({ textLayerDiv, pageIndex, viewport, enhanceTextSelection, + eventBus, }); } } diff --git a/web/ui_utils.js b/web/ui_utils.js index 648b65302..e258080d2 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -757,9 +757,8 @@ const animationStarted = new Promise(function(resolve) { }); /** - * Simple event bus for an application. Listeners are attached using the - * `on` and `off` methods. To raise an event, the `dispatch` method shall be - * used. + * Simple event bus for an application. Listeners are attached using the `on` + * and `off` methods. To raise an event, the `dispatch` method shall be used. */ class EventBus { constructor({ dispatchToDOM = false } = {}) { @@ -832,6 +831,9 @@ class EventBus { let globalEventBus = null; function getGlobalEventBus(dispatchToDOM = false) { + console.error( + "getGlobalEventBus is deprecated, use a manually created EventBus instance instead." + ); if (!globalEventBus) { globalEventBus = new EventBus({ dispatchToDOM }); } diff --git a/web/viewer.js b/web/viewer.js index e90526ed1..35d919b80 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -60,7 +60,7 @@ function getViewerConfiguration() { appContainer: document.body, mainContainer: document.getElementById("viewerContainer"), viewerContainer: document.getElementById("viewer"), - eventBus: null, // Using global event bus with (optional) DOM events. + eventBus: null, toolbar: { container: document.getElementById("toolbarViewer"), numPages: document.getElementById("numPages"), From 4a1b056c82f7dfa5e2532445c65d5e1917f3c16d Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 26 Feb 2020 23:33:27 +0100 Subject: [PATCH 2/2] Re-factor the `EventBus` to allow servicing of "external" event listeners *after* the viewer components have updated Since the goal has always been, essentially since the `EventBus` abstraction was added, to remove all dispatching of DOM events[1] from the viewer components this patch tries to address one thing that came up when updating the examples: The DOM events are always dispatched last, and it's thus guaranteed that all internal event listeners have been invoked first. However, there's no such guarantees with the general `EventBus` functionality and the order in which event listeners are invoked is *not* specified. With the promotion of the `EventBus` in the examples, over DOM events, it seems like a good idea to at least *try* to keep this ordering invariant[2] intact. Obviously this won't prevent anyone from manually calling the new *internal* viewer component methods on the `EventBus`, but hopefully that won't be too common since any existing third-party code would obviously use the `on`/`off` methods and that all of the examples shows the *correct* usage (which should be similarily documented on the "Third party viewer usage" Wiki-page). --- [1] Looking at the various Firefox-tests, I'm not sure that it'll be possible to (easily) re-write all of them to not rely on DOM events (since getting access to `PDFViewerApplication` might be generally difficult/messy depending on scopes). In any case, even if technically feasible, it would most likely add *a lot* of complication that may not be desireable in the various Firefox-tests. All-in-all, I'd be fine with keeping the DOM events only for the `MOZCENTRAL` target and gated on `Cu.isInAutomation` (or similar) rather than a preference. [2] I wouldn't expect any *real* bugs in a custom implementation, simply based on event ordering, but it nonetheless seem like a good idea if any "external" events are still handled last. --- web/app.js | 152 ++++++++++++++++----------------- web/base_viewer.js | 10 +-- web/pdf_attachment_viewer.js | 2 +- web/pdf_cursor_tools.js | 4 +- web/pdf_document_properties.js | 4 +- web/pdf_find_bar.js | 2 +- web/pdf_find_controller.js | 2 +- web/pdf_history.js | 12 +-- web/pdf_outline_viewer.js | 2 +- web/pdf_sidebar.js | 6 +- web/pdf_sidebar_resizer.js | 4 +- web/pdf_single_page_viewer.js | 2 +- web/secondary_toolbar.js | 14 +-- web/text_layer_builder.js | 4 +- web/toolbar.js | 2 +- web/ui_utils.js | 70 ++++++++++++--- 16 files changed, 168 insertions(+), 124 deletions(-) diff --git a/web/app.js b/web/app.js index 0441e8f21..12e62eb01 100644 --- a/web/app.js +++ b/web/app.js @@ -1597,45 +1597,45 @@ const PDFViewerApplication = { _boundEvents.beforePrint = this.beforePrint.bind(this); _boundEvents.afterPrint = this.afterPrint.bind(this); - eventBus.on("resize", webViewerResize); - eventBus.on("hashchange", webViewerHashchange); - eventBus.on("beforeprint", _boundEvents.beforePrint); - eventBus.on("afterprint", _boundEvents.afterPrint); - eventBus.on("pagerendered", webViewerPageRendered); - eventBus.on("updateviewarea", webViewerUpdateViewarea); - eventBus.on("pagechanging", webViewerPageChanging); - eventBus.on("scalechanging", webViewerScaleChanging); - eventBus.on("rotationchanging", webViewerRotationChanging); - eventBus.on("sidebarviewchanged", webViewerSidebarViewChanged); - eventBus.on("pagemode", webViewerPageMode); - eventBus.on("namedaction", webViewerNamedAction); - eventBus.on("presentationmodechanged", webViewerPresentationModeChanged); - eventBus.on("presentationmode", webViewerPresentationMode); - eventBus.on("openfile", webViewerOpenFile); - eventBus.on("print", webViewerPrint); - eventBus.on("download", webViewerDownload); - eventBus.on("firstpage", webViewerFirstPage); - eventBus.on("lastpage", webViewerLastPage); - eventBus.on("nextpage", webViewerNextPage); - eventBus.on("previouspage", webViewerPreviousPage); - eventBus.on("zoomin", webViewerZoomIn); - eventBus.on("zoomout", webViewerZoomOut); - eventBus.on("zoomreset", webViewerZoomReset); - eventBus.on("pagenumberchanged", webViewerPageNumberChanged); - eventBus.on("scalechanged", webViewerScaleChanged); - eventBus.on("rotatecw", webViewerRotateCw); - eventBus.on("rotateccw", webViewerRotateCcw); - eventBus.on("switchscrollmode", webViewerSwitchScrollMode); - eventBus.on("scrollmodechanged", webViewerScrollModeChanged); - eventBus.on("switchspreadmode", webViewerSwitchSpreadMode); - eventBus.on("spreadmodechanged", webViewerSpreadModeChanged); - eventBus.on("documentproperties", webViewerDocumentProperties); - eventBus.on("find", webViewerFind); - eventBus.on("findfromurlhash", webViewerFindFromUrlHash); - eventBus.on("updatefindmatchescount", webViewerUpdateFindMatchesCount); - eventBus.on("updatefindcontrolstate", webViewerUpdateFindControlState); + eventBus._on("resize", webViewerResize); + eventBus._on("hashchange", webViewerHashchange); + eventBus._on("beforeprint", _boundEvents.beforePrint); + eventBus._on("afterprint", _boundEvents.afterPrint); + eventBus._on("pagerendered", webViewerPageRendered); + eventBus._on("updateviewarea", webViewerUpdateViewarea); + eventBus._on("pagechanging", webViewerPageChanging); + eventBus._on("scalechanging", webViewerScaleChanging); + eventBus._on("rotationchanging", webViewerRotationChanging); + eventBus._on("sidebarviewchanged", webViewerSidebarViewChanged); + eventBus._on("pagemode", webViewerPageMode); + eventBus._on("namedaction", webViewerNamedAction); + eventBus._on("presentationmodechanged", webViewerPresentationModeChanged); + eventBus._on("presentationmode", webViewerPresentationMode); + eventBus._on("openfile", webViewerOpenFile); + eventBus._on("print", webViewerPrint); + eventBus._on("download", webViewerDownload); + eventBus._on("firstpage", webViewerFirstPage); + eventBus._on("lastpage", webViewerLastPage); + eventBus._on("nextpage", webViewerNextPage); + eventBus._on("previouspage", webViewerPreviousPage); + eventBus._on("zoomin", webViewerZoomIn); + eventBus._on("zoomout", webViewerZoomOut); + eventBus._on("zoomreset", webViewerZoomReset); + eventBus._on("pagenumberchanged", webViewerPageNumberChanged); + eventBus._on("scalechanged", webViewerScaleChanged); + eventBus._on("rotatecw", webViewerRotateCw); + eventBus._on("rotateccw", webViewerRotateCcw); + eventBus._on("switchscrollmode", webViewerSwitchScrollMode); + eventBus._on("scrollmodechanged", webViewerScrollModeChanged); + eventBus._on("switchspreadmode", webViewerSwitchSpreadMode); + eventBus._on("spreadmodechanged", webViewerSpreadModeChanged); + eventBus._on("documentproperties", webViewerDocumentProperties); + eventBus._on("find", webViewerFind); + eventBus._on("findfromurlhash", webViewerFindFromUrlHash); + eventBus._on("updatefindmatchescount", webViewerUpdateFindMatchesCount); + eventBus._on("updatefindcontrolstate", webViewerUpdateFindControlState); if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { - eventBus.on("fileinputchange", webViewerFileInputChange); + eventBus._on("fileinputchange", webViewerFileInputChange); } }, @@ -1671,45 +1671,45 @@ const PDFViewerApplication = { unbindEvents() { const { eventBus, _boundEvents } = this; - eventBus.off("resize", webViewerResize); - eventBus.off("hashchange", webViewerHashchange); - eventBus.off("beforeprint", _boundEvents.beforePrint); - eventBus.off("afterprint", _boundEvents.afterPrint); - eventBus.off("pagerendered", webViewerPageRendered); - eventBus.off("updateviewarea", webViewerUpdateViewarea); - eventBus.off("pagechanging", webViewerPageChanging); - eventBus.off("scalechanging", webViewerScaleChanging); - eventBus.off("rotationchanging", webViewerRotationChanging); - eventBus.off("sidebarviewchanged", webViewerSidebarViewChanged); - eventBus.off("pagemode", webViewerPageMode); - eventBus.off("namedaction", webViewerNamedAction); - eventBus.off("presentationmodechanged", webViewerPresentationModeChanged); - eventBus.off("presentationmode", webViewerPresentationMode); - eventBus.off("openfile", webViewerOpenFile); - eventBus.off("print", webViewerPrint); - eventBus.off("download", webViewerDownload); - eventBus.off("firstpage", webViewerFirstPage); - eventBus.off("lastpage", webViewerLastPage); - eventBus.off("nextpage", webViewerNextPage); - eventBus.off("previouspage", webViewerPreviousPage); - eventBus.off("zoomin", webViewerZoomIn); - eventBus.off("zoomout", webViewerZoomOut); - eventBus.off("zoomreset", webViewerZoomReset); - eventBus.off("pagenumberchanged", webViewerPageNumberChanged); - eventBus.off("scalechanged", webViewerScaleChanged); - eventBus.off("rotatecw", webViewerRotateCw); - eventBus.off("rotateccw", webViewerRotateCcw); - eventBus.off("switchscrollmode", webViewerSwitchScrollMode); - eventBus.off("scrollmodechanged", webViewerScrollModeChanged); - eventBus.off("switchspreadmode", webViewerSwitchSpreadMode); - eventBus.off("spreadmodechanged", webViewerSpreadModeChanged); - eventBus.off("documentproperties", webViewerDocumentProperties); - eventBus.off("find", webViewerFind); - eventBus.off("findfromurlhash", webViewerFindFromUrlHash); - eventBus.off("updatefindmatchescount", webViewerUpdateFindMatchesCount); - eventBus.off("updatefindcontrolstate", webViewerUpdateFindControlState); + eventBus._off("resize", webViewerResize); + eventBus._off("hashchange", webViewerHashchange); + eventBus._off("beforeprint", _boundEvents.beforePrint); + eventBus._off("afterprint", _boundEvents.afterPrint); + eventBus._off("pagerendered", webViewerPageRendered); + eventBus._off("updateviewarea", webViewerUpdateViewarea); + eventBus._off("pagechanging", webViewerPageChanging); + eventBus._off("scalechanging", webViewerScaleChanging); + eventBus._off("rotationchanging", webViewerRotationChanging); + eventBus._off("sidebarviewchanged", webViewerSidebarViewChanged); + eventBus._off("pagemode", webViewerPageMode); + eventBus._off("namedaction", webViewerNamedAction); + eventBus._off("presentationmodechanged", webViewerPresentationModeChanged); + eventBus._off("presentationmode", webViewerPresentationMode); + eventBus._off("openfile", webViewerOpenFile); + eventBus._off("print", webViewerPrint); + eventBus._off("download", webViewerDownload); + eventBus._off("firstpage", webViewerFirstPage); + eventBus._off("lastpage", webViewerLastPage); + eventBus._off("nextpage", webViewerNextPage); + eventBus._off("previouspage", webViewerPreviousPage); + eventBus._off("zoomin", webViewerZoomIn); + eventBus._off("zoomout", webViewerZoomOut); + eventBus._off("zoomreset", webViewerZoomReset); + eventBus._off("pagenumberchanged", webViewerPageNumberChanged); + eventBus._off("scalechanged", webViewerScaleChanged); + eventBus._off("rotatecw", webViewerRotateCw); + eventBus._off("rotateccw", webViewerRotateCcw); + eventBus._off("switchscrollmode", webViewerSwitchScrollMode); + eventBus._off("scrollmodechanged", webViewerScrollModeChanged); + eventBus._off("switchspreadmode", webViewerSwitchSpreadMode); + eventBus._off("spreadmodechanged", webViewerSpreadModeChanged); + eventBus._off("documentproperties", webViewerDocumentProperties); + eventBus._off("find", webViewerFind); + eventBus._off("findfromurlhash", webViewerFindFromUrlHash); + eventBus._off("updatefindmatchescount", webViewerUpdateFindMatchesCount); + eventBus._off("updatefindcontrolstate", webViewerUpdateFindControlState); if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { - eventBus.off("fileinputchange", webViewerFileInputChange); + eventBus._off("fileinputchange", webViewerFileInputChange); } _boundEvents.beforePrint = null; diff --git a/web/base_viewer.js b/web/base_viewer.js index 77146eb9b..21141bd52 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -426,7 +426,7 @@ class BaseViewer { // evicted from the buffer and destroyed even if we pause its rendering. this._buffer.push(pageView); }; - this.eventBus.on("pagerender", this._onBeforeDraw); + this.eventBus._on("pagerender", this._onBeforeDraw); this._onAfterDraw = evt => { if (evt.cssTransform || onePageRenderedCapability.settled) { @@ -434,10 +434,10 @@ class BaseViewer { } onePageRenderedCapability.resolve(); - this.eventBus.off("pagerendered", this._onAfterDraw); + this.eventBus._off("pagerendered", this._onAfterDraw); this._onAfterDraw = null; }; - this.eventBus.on("pagerendered", this._onAfterDraw); + this.eventBus._on("pagerendered", this._onAfterDraw); // Fetch a single page so we can get a viewport that will be the default // viewport for all pages @@ -582,11 +582,11 @@ class BaseViewer { this._spreadMode = SpreadMode.NONE; if (this._onBeforeDraw) { - this.eventBus.off("pagerender", this._onBeforeDraw); + this.eventBus._off("pagerender", this._onBeforeDraw); this._onBeforeDraw = null; } if (this._onAfterDraw) { - this.eventBus.off("pagerendered", this._onAfterDraw); + this.eventBus._off("pagerendered", this._onAfterDraw); this._onAfterDraw = null; } // Remove the pages from the DOM... diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index a7e6a2411..5df8703bf 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -43,7 +43,7 @@ class PDFAttachmentViewer { this.reset(); - this.eventBus.on( + this.eventBus._on( "fileattachmentannotation", this._appendAttachment.bind(this) ); diff --git a/web/pdf_cursor_tools.js b/web/pdf_cursor_tools.js index c9e23f996..1b9ef6f43 100644 --- a/web/pdf_cursor_tools.js +++ b/web/pdf_cursor_tools.js @@ -122,11 +122,11 @@ class PDFCursorTools { * @private */ _addEventListeners() { - this.eventBus.on("switchcursortool", evt => { + this.eventBus._on("switchcursortool", evt => { this.switchTool(evt.tool); }); - this.eventBus.on("presentationmodechanged", evt => { + this.eventBus._on("presentationmodechanged", evt => { if (evt.switchInProgress) { return; } diff --git a/web/pdf_document_properties.js b/web/pdf_document_properties.js index 10c00a879..d55e30202 100644 --- a/web/pdf_document_properties.js +++ b/web/pdf_document_properties.js @@ -84,10 +84,10 @@ class PDFDocumentProperties { ); if (eventBus) { - eventBus.on("pagechanging", evt => { + eventBus._on("pagechanging", evt => { this._currentPageNumber = evt.pageNumber; }); - eventBus.on("rotationchanging", evt => { + eventBus._on("rotationchanging", evt => { this._pagesRotation = evt.pagesRotation; }); } diff --git a/web/pdf_find_bar.js b/web/pdf_find_bar.js index 5ace2fc74..2a42d8573 100644 --- a/web/pdf_find_bar.js +++ b/web/pdf_find_bar.js @@ -83,7 +83,7 @@ class PDFFindBar { this.dispatchEvent("entirewordchange"); }); - this.eventBus.on("resize", this._adjustWidth.bind(this)); + this.eventBus._on("resize", this._adjustWidth.bind(this)); } reset() { diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 53a560260..6e46f6331 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -72,7 +72,7 @@ class PDFFindController { this._eventBus = eventBus || getGlobalEventBus(); this._reset(); - eventBus.on("findbarclose", this._onFindBarClose.bind(this)); + eventBus._on("findbarclose", this._onFindBarClose.bind(this)); } get highlightMatches() { diff --git a/web/pdf_history.js b/web/pdf_history.js index cf1e35fed..4e8127b15 100644 --- a/web/pdf_history.js +++ b/web/pdf_history.js @@ -69,17 +69,17 @@ class PDFHistory { this._isViewerInPresentationMode = false; // Ensure that we don't miss either a 'presentationmodechanged' or a // 'pagesinit' event, by registering the listeners immediately. - this.eventBus.on("presentationmodechanged", evt => { + this.eventBus._on("presentationmodechanged", evt => { this._isViewerInPresentationMode = evt.active || evt.switchInProgress; }); - this.eventBus.on("pagesinit", () => { + this.eventBus._on("pagesinit", () => { this._isPagesLoaded = false; const onPagesLoaded = evt => { - this.eventBus.off("pagesloaded", onPagesLoaded); + this.eventBus._off("pagesloaded", onPagesLoaded); this._isPagesLoaded = !!evt.pagesCount; }; - this.eventBus.on("pagesloaded", onPagesLoaded); + this.eventBus._on("pagesloaded", onPagesLoaded); }); } @@ -684,7 +684,7 @@ class PDFHistory { pageHide: this._pageHide.bind(this), }; - this.eventBus.on("updateviewarea", this._boundEvents.updateViewarea); + this.eventBus._on("updateviewarea", this._boundEvents.updateViewarea); window.addEventListener("popstate", this._boundEvents.popState); window.addEventListener("pagehide", this._boundEvents.pageHide); } @@ -696,7 +696,7 @@ class PDFHistory { if (!this._boundEvents) { return; // The event listeners were already removed. } - this.eventBus.off("updateviewarea", this._boundEvents.updateViewarea); + this.eventBus._off("updateviewarea", this._boundEvents.updateViewarea); window.removeEventListener("popstate", this._boundEvents.popState); window.removeEventListener("pagehide", this._boundEvents.pageHide); diff --git a/web/pdf_outline_viewer.js b/web/pdf_outline_viewer.js index a50cb977e..672e3b76c 100644 --- a/web/pdf_outline_viewer.js +++ b/web/pdf_outline_viewer.js @@ -40,7 +40,7 @@ class PDFOutlineViewer { this.reset(); - eventBus.on("toggleoutlinetree", this.toggleOutlineTree.bind(this)); + eventBus._on("toggleoutlinetree", this.toggleOutlineTree.bind(this)); } reset() { diff --git a/web/pdf_sidebar.js b/web/pdf_sidebar.js index a06d5a8aa..a10358b67 100644 --- a/web/pdf_sidebar.js +++ b/web/pdf_sidebar.js @@ -430,7 +430,7 @@ class PDFSidebar { }); // Disable/enable views. - this.eventBus.on("outlineloaded", evt => { + this.eventBus._on("outlineloaded", evt => { const outlineCount = evt.outlineCount; this.outlineButton.disabled = !outlineCount; @@ -444,7 +444,7 @@ class PDFSidebar { } }); - this.eventBus.on("attachmentsloaded", evt => { + this.eventBus._on("attachmentsloaded", evt => { if (evt.attachmentsCount) { this.attachmentsButton.disabled = false; @@ -473,7 +473,7 @@ class PDFSidebar { }); // Update the thumbnailViewer, if visible, when exiting presentation mode. - this.eventBus.on("presentationmodechanged", evt => { + this.eventBus._on("presentationmodechanged", evt => { if (!evt.active && !evt.switchInProgress && this.isThumbnailViewVisible) { this._updateThumbnailViewer(); } diff --git a/web/pdf_sidebar_resizer.js b/web/pdf_sidebar_resizer.js index 77b0a4516..668c59dcb 100644 --- a/web/pdf_sidebar_resizer.js +++ b/web/pdf_sidebar_resizer.js @@ -151,11 +151,11 @@ class PDFSidebarResizer { window.addEventListener("mouseup", _boundEvents.mouseUp); }); - this.eventBus.on("sidebarviewchanged", evt => { + this.eventBus._on("sidebarviewchanged", evt => { this.sidebarOpen = !!(evt && evt.view); }); - this.eventBus.on("resize", evt => { + this.eventBus._on("resize", evt => { // When the *entire* viewer is resized, such that it becomes narrower, // ensure that the sidebar doesn't end up being too wide. if (!evt || evt.source !== window) { diff --git a/web/pdf_single_page_viewer.js b/web/pdf_single_page_viewer.js index e6dabb391..3db66f3b1 100644 --- a/web/pdf_single_page_viewer.js +++ b/web/pdf_single_page_viewer.js @@ -20,7 +20,7 @@ class PDFSinglePageViewer extends BaseViewer { constructor(options) { super(options); - this.eventBus.on("pagesinit", evt => { + this.eventBus._on("pagesinit", evt => { // Since the pages are placed in a `DocumentFragment`, make sure that // the current page becomes visible upon loading of the document. this._ensurePageViewVisible(); diff --git a/web/secondary_toolbar.js b/web/secondary_toolbar.js index 32bc4601f..0dd75f815 100644 --- a/web/secondary_toolbar.js +++ b/web/secondary_toolbar.js @@ -159,11 +159,11 @@ class SecondaryToolbar { this._bindSpreadModeListener(options); // Bind the event listener for adjusting the 'max-height' of the toolbar. - this.eventBus.on("resize", this._setMaxHeight.bind(this)); + this.eventBus._on("resize", this._setMaxHeight.bind(this)); // Hide the Scroll/Spread mode buttons, when they're not applicable to the // current `BaseViewer` instance (in particular `PDFSinglePageViewer`). - this.eventBus.on("baseviewerinit", evt => { + this.eventBus._on("baseviewerinit", evt => { if (evt.source instanceof PDFSinglePageViewer) { this.toolbarButtonContainer.classList.add( "hiddenScrollModeButtons", @@ -233,7 +233,7 @@ class SecondaryToolbar { } _bindCursorToolsListener(buttons) { - this.eventBus.on("cursortoolchanged", function({ tool }) { + this.eventBus._on("cursortoolchanged", function({ tool }) { buttons.cursorSelectToolButton.classList.toggle( "toggled", tool === CursorTool.SELECT @@ -267,9 +267,9 @@ class SecondaryToolbar { buttons.spreadOddButton.disabled = isScrollModeHorizontal; buttons.spreadEvenButton.disabled = isScrollModeHorizontal; } - this.eventBus.on("scrollmodechanged", scrollModeChanged); + this.eventBus._on("scrollmodechanged", scrollModeChanged); - this.eventBus.on("secondarytoolbarreset", evt => { + this.eventBus._on("secondarytoolbarreset", evt => { if (evt.source === this) { scrollModeChanged({ mode: ScrollMode.VERTICAL }); } @@ -291,9 +291,9 @@ class SecondaryToolbar { mode === SpreadMode.EVEN ); } - this.eventBus.on("spreadmodechanged", spreadModeChanged); + this.eventBus._on("spreadmodechanged", spreadModeChanged); - this.eventBus.on("secondarytoolbarreset", evt => { + this.eventBus._on("secondarytoolbarreset", evt => { if (evt.source === this) { spreadModeChanged({ mode: SpreadMode.NONE }); } diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index ec56a376c..6c3be2f54 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -123,7 +123,7 @@ class TextLayerBuilder { this._updateMatches(); } }; - this.eventBus.on( + this.eventBus._on( "updatetextlayermatches", this._onUpdateTextLayerMatches ); @@ -139,7 +139,7 @@ class TextLayerBuilder { this.textLayerRenderTask = null; } if (this._onUpdateTextLayerMatches) { - this.eventBus.off( + this.eventBus._off( "updatetextlayermatches", this._onUpdateTextLayerMatches ); diff --git a/web/toolbar.js b/web/toolbar.js index bd6fe5484..1937292ee 100644 --- a/web/toolbar.js +++ b/web/toolbar.js @@ -159,7 +159,7 @@ class Toolbar { // Suppress context menus for some controls. scaleSelect.oncontextmenu = noContextMenuHandler; - this.eventBus.on("localized", () => { + this.eventBus._on("localized", () => { this._wasLocalized = true; this._adjustScaleWidth(); this._updateUIState(true); diff --git a/web/ui_utils.js b/web/ui_utils.js index e258080d2..d43564141 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -766,22 +766,20 @@ class EventBus { this._dispatchToDOM = dispatchToDOM === true; } + /** + * @param {string} eventName + * @param {function} listener + */ on(eventName, listener) { - let eventListeners = this._listeners[eventName]; - if (!eventListeners) { - eventListeners = []; - this._listeners[eventName] = eventListeners; - } - eventListeners.push(listener); + this._on(eventName, listener, { external: true }); } + /** + * @param {string} eventName + * @param {function} listener + */ off(eventName, listener) { - const eventListeners = this._listeners[eventName]; - let i; - if (!eventListeners || (i = eventListeners.indexOf(listener)) < 0) { - return; - } - eventListeners.splice(i, 1); + this._off(eventName, listener, { external: true }); } dispatch(eventName) { @@ -795,16 +793,62 @@ class EventBus { } // Passing all arguments after the eventName to the listeners. const args = Array.prototype.slice.call(arguments, 1); + let externalListeners; // Making copy of the listeners array in case if it will be modified // during dispatch. - eventListeners.slice(0).forEach(function(listener) { + eventListeners.slice(0).forEach(function({ listener, external }) { + if (external) { + if (!externalListeners) { + externalListeners = []; + } + externalListeners.push(listener); + return; + } listener.apply(null, args); }); + // Dispatch any "external" listeners *after* the internal ones, to give the + // viewer components time to handle events and update their state first. + if (externalListeners) { + externalListeners.forEach(function(listener) { + listener.apply(null, args); + }); + externalListeners = null; + } if (this._dispatchToDOM) { this._dispatchDOMEvent(eventName, args); } } + /** + * @ignore + */ + _on(eventName, listener, options = null) { + let eventListeners = this._listeners[eventName]; + if (!eventListeners) { + this._listeners[eventName] = eventListeners = []; + } + eventListeners.push({ + listener, + external: options ? options.external : false, + }); + } + + /** + * @ignore + */ + _off(eventName, listener, options = null) { + const eventListeners = this._listeners[eventName]; + if (!eventListeners) { + return; + } + for (let i = 0, ii = eventListeners.length; i < ii; i++) { + if (eventListeners[i].listener === listener) { + eventListeners.splice(i, 1); + return; + } + } + } + /** * @private */