From 7fd3db99778a0cf7a0e74bd3bd5a3cf2f5970b84 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 25 Apr 2016 17:57:15 -0500 Subject: [PATCH 1/2] Adds EventBus. --- test/unit/ui_utils_spec.js | 88 ++++++++++++++++++- web/app.js | 163 +++++++++++++++++++++++------------ web/dom_events.js | 152 ++++++++++++++++++++++++++++++++ web/hand_tool.js | 10 ++- web/pdf_attachment_viewer.js | 7 +- web/pdf_find_bar.js | 7 +- web/pdf_history.js | 14 +-- web/pdf_link_service.js | 33 ++++--- web/pdf_outline_viewer.js | 7 +- web/pdf_page_view.js | 23 ++--- web/pdf_presentation_mode.js | 7 +- web/pdf_sidebar.js | 22 ++--- web/pdf_viewer.component.js | 1 + web/pdf_viewer.js | 65 +++++++------- web/text_layer_builder.js | 17 ++-- web/ui_utils.js | 44 ++++++++++ web/viewer.js | 1 + 17 files changed, 510 insertions(+), 151 deletions(-) create mode 100644 web/dom_events.js diff --git a/test/unit/ui_utils_spec.js b/test/unit/ui_utils_spec.js index 597199aba..d93b16fa5 100644 --- a/test/unit/ui_utils_spec.js +++ b/test/unit/ui_utils_spec.js @@ -1,4 +1,4 @@ -/* globals expect, it, describe, binarySearchFirstItem */ +/* globals expect, it, describe, binarySearchFirstItem, EventBus */ 'use strict'; @@ -30,5 +30,91 @@ describe('ui_utils', function() { expect(binarySearchFirstItem([4, 5, 6], isGreater3)).toEqual(0); }); }); + + describe('EventBus', function () { + it('dispatch event', function () { + var eventBus = new EventBus(); + var count = 0; + eventBus.on('test', function () { + count++; + }); + eventBus.dispatch('test'); + expect(count).toEqual(1); + }); + it('dispatch different event', function () { + var eventBus = new EventBus(); + var count = 0; + eventBus.on('test', function () { + count++; + }); + eventBus.dispatch('nottest'); + expect(count).toEqual(0); + }); + it('dispatch event multiple times', function () { + var eventBus = new EventBus(); + var count = 0; + eventBus.dispatch('test'); + eventBus.on('test', function () { + count++; + }); + eventBus.dispatch('test'); + eventBus.dispatch('test'); + expect(count).toEqual(2); + }); + it('dispatch event to multiple handlers', function () { + var eventBus = new EventBus(); + var count = 0; + eventBus.on('test', function () { + count++; + }); + eventBus.on('test', function () { + count++; + }); + eventBus.dispatch('test'); + expect(count).toEqual(2); + }); + it('dispatch to detached', function () { + var eventBus = new EventBus(); + var count = 0; + var listener = function () { + count++; + }; + eventBus.on('test', listener); + eventBus.dispatch('test'); + eventBus.off('test', listener); + eventBus.dispatch('test'); + expect(count).toEqual(1); + }); + it('dispatch to wrong detached', function () { + var eventBus = new EventBus(); + var count = 0; + eventBus.on('test', function () { + count++; + }); + eventBus.dispatch('test'); + eventBus.off('test', function () { + count++; + }); + eventBus.dispatch('test'); + expect(count).toEqual(2); + }); + it('dispatch to detached during handling', function () { + var eventBus = new EventBus(); + var count = 0; + var listener1 = function () { + eventBus.off('test', listener2); + count++; + }; + var listener2 = function () { + eventBus.off('test', listener1); + count++; + }; + eventBus.on('test', listener1); + eventBus.on('test', listener2); + eventBus.dispatch('test'); + eventBus.dispatch('test'); + expect(count).toEqual(2); + }); + }); }); diff --git a/web/app.js b/web/app.js index 98c784779..e2d1b9cce 100644 --- a/web/app.js +++ b/web/app.js @@ -28,7 +28,7 @@ 'pdfjs-web/pdf_rendering_queue', 'pdfjs-web/pdf_link_service', 'pdfjs-web/pdf_outline_viewer', 'pdfjs-web/overlay_manager', 'pdfjs-web/pdf_attachment_viewer', 'pdfjs-web/pdf_find_controller', - 'pdfjs-web/pdf_find_bar', 'pdfjs-web/pdfjs'], + 'pdfjs-web/pdf_find_bar', 'pdfjs-web/dom_events', 'pdfjs-web/pdfjs'], factory); } else if (typeof exports !== 'undefined') { factory(exports, require('./ui_utils.js'), require('./download_manager.js'), @@ -41,7 +41,7 @@ require('./pdf_link_service.js'), require('./pdf_outline_viewer.js'), require('./overlay_manager.js'), require('./pdf_attachment_viewer.js'), require('./pdf_find_controller.js'), require('./pdf_find_bar.js'), - require('./pdfjs.js')); + require('./dom_events.js'), require('./pdfjs.js')); } else { factory((root.pdfjsWebApp = {}), root.pdfjsWebUIUtils, root.pdfjsWebDownloadManager, root.pdfjsWebPDFHistory, @@ -53,7 +53,7 @@ root.pdfjsWebPDFRenderingQueue, root.pdfjsWebPDFLinkService, root.pdfjsWebPDFOutlineViewer, root.pdfjsWebOverlayManager, root.pdfjsWebPDFAttachmentViewer, root.pdfjsWebPDFFindController, - root.pdfjsWebPDFFindBar, root.pdfjsWebPDFJS); + root.pdfjsWebPDFFindBar, root.pdfjsWebDOMEvents, root.pdfjsWebPDFJS); } }(this, function (exports, uiUtilsLib, downloadManagerLib, pdfHistoryLib, preferencesLib, pdfSidebarLib, viewHistoryLib, @@ -62,7 +62,7 @@ pdfViewerLib, pdfRenderingQueueLib, pdfLinkServiceLib, pdfOutlineViewerLib, overlayManagerLib, pdfAttachmentViewerLib, pdfFindControllerLib, pdfFindBarLib, - pdfjsLib) { + domEventsLib, pdfjsLib) { var UNKNOWN_SCALE = uiUtilsLib.UNKNOWN_SCALE; var DEFAULT_SCALE_VALUE = uiUtilsLib.DEFAULT_SCALE_VALUE; @@ -92,6 +92,7 @@ var OverlayManager = overlayManagerLib.OverlayManager; var PDFAttachmentViewer = pdfAttachmentViewerLib.PDFAttachmentViewer; var PDFFindController = pdfFindControllerLib.PDFFindController; var PDFFindBar = pdfFindBarLib.PDFFindBar; +var getGlobalEventBus = domEventsLib.getGlobalEventBus; var DEFAULT_SCALE_DELTA = 1.1; var MIN_SCALE = 0.25; @@ -166,6 +167,8 @@ var PDFViewerApplication = { store: null, /** @type {DownloadManager} */ downloadManager: null, + /** @type {EventBus} */ + eventBus: null, pageRotation: 0, isInitialViewSet: false, animationStartedPromise: null, @@ -182,11 +185,17 @@ var PDFViewerApplication = { configure(pdfjsLib.PDFJS); this.appConfig = appConfig; + var eventBus = appConfig.eventBus || getGlobalEventBus(); + this.eventBus = eventBus; + this.bindEvents(); + var pdfRenderingQueue = new PDFRenderingQueue(); pdfRenderingQueue.onIdle = this.cleanup.bind(this); this.pdfRenderingQueue = pdfRenderingQueue; - var pdfLinkService = new PDFLinkService(); + var pdfLinkService = new PDFLinkService({ + eventBus: eventBus + }); this.pdfLinkService = pdfLinkService; var downloadManager = this.externalServices.createDownloadManager(); @@ -197,6 +206,7 @@ var PDFViewerApplication = { this.pdfViewer = new PDFViewer({ container: container, viewer: viewer, + eventBus: eventBus, renderingQueue: pdfRenderingQueue, linkService: pdfLinkService, downloadManager: downloadManager @@ -216,7 +226,8 @@ var PDFViewerApplication = { this.preferences = Preferences; this.pdfHistory = new PDFHistory({ - linkService: pdfLinkService + linkService: pdfLinkService, + eventBus: this.eventBus }); pdfLinkService.setHistory(this.pdfHistory); @@ -244,12 +255,14 @@ var PDFViewerApplication = { // FIXME better PDFFindBar constructor parameters var findBarConfig = Object.create(appConfig.findBar); findBarConfig.findController = this.findController; + findBarConfig.eventBus = this.eventBus; this.findBar = new PDFFindBar(findBarConfig); this.overlayManager = OverlayManager; this.handTool = new HandTool({ container: container, + eventBus: this.eventBus, toggleHandTool: appConfig.secondaryToolbar.toggleHandTool }); @@ -265,6 +278,7 @@ var PDFViewerApplication = { container: container, viewer: viewer, pdfViewer: this.pdfViewer, + eventBus: this.eventBus, contextMenuItems: [ { element: appConfig.fullscreen.contextFirstPage, handler: toolbar.firstPageClick.bind(toolbar) }, @@ -282,11 +296,13 @@ var PDFViewerApplication = { this.pdfOutlineViewer = new PDFOutlineViewer({ container: appConfig.sidebar.outlineView, + eventBus: this.eventBus, linkService: pdfLinkService, }); this.pdfAttachmentViewer = new PDFAttachmentViewer({ container: appConfig.sidebar.attachmentsView, + eventBus: this.eventBus, downloadManager: downloadManager }); @@ -295,6 +311,7 @@ var PDFViewerApplication = { sidebarConfig.pdfViewer = this.pdfViewer; sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer; sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer; + sidebarConfig.eventBus = this.eventBus; this.pdfSidebar = new PDFSidebar(sidebarConfig); this.pdfSidebar.onToggled = this.forceRendering.bind(this); @@ -856,9 +873,7 @@ var PDFViewerApplication = { firstPagePromise.then(function(pdfPage) { downloadedPromise.then(function () { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('documentload', true, true, {}); - window.dispatchEvent(event); + self.eventBus.dispatch('documentload', {source: self}); }); self.loadingBar.setWidth(self.appConfig.viewerContainer); @@ -1215,6 +1230,28 @@ var PDFViewerApplication = { return; } this.pdfPresentationMode.mouseScroll(delta); + }, + + bindEvents: function pdfViewBindEvents() { + var eventBus = this.eventBus; + + eventBus.on('resize', webViewerResize); + eventBus.on('localized', webViewerLocalized); + eventBus.on('hashchange', webViewerHashchange); + eventBus.on('beforeprint', this.beforePrint.bind(this)); + eventBus.on('afterprint', this.afterPrint.bind(this)); + eventBus.on('pagerendered', webViewerPageRendered); + eventBus.on('textlayerrendered', webViewerTextLayerRendered); + eventBus.on('updateviewarea', webViewerUpdateViewarea); + eventBus.on('pagechange', webViewerPageChange); + eventBus.on('scalechange', webViewerScaleChange); + eventBus.on('sidebarviewchanged', webViewerSidebarViewChanged); + eventBus.on('pagemode', webViewerPageMode); + eventBus.on('namedaction', webViewerNamedAction); + eventBus.on('presentationmodechanged', webViewerPresentationModeChanged); +//#if GENERIC + eventBus.on('fileinputchange', webViewerFileInputChange); +//#endif } }; @@ -1390,9 +1427,7 @@ function webViewerInitialized() { appConfig.sidebar.mainContainer.addEventListener('transitionend', function(e) { if (e.target === /* mainContainer */ this) { - var event = document.createEvent('UIEvents'); - event.initUIEvent('resize', false, false, window, 0); - window.dispatchEvent(event); + PDFViewerApplication.eventBus.dispatch('resize'); } }, true); @@ -1486,8 +1521,8 @@ function webViewerInitialized() { //#endif } -document.addEventListener('pagerendered', function (e) { - var pageNumber = e.detail.pageNumber; +function webViewerPageRendered(e) { + var pageNumber = e.pageNumber; var pageIndex = pageNumber - 1; var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex); @@ -1531,10 +1566,10 @@ document.addEventListener('pagerendered', function (e) { }); }); //#endif -}, true); +} -document.addEventListener('textlayerrendered', function (e) { - var pageIndex = e.detail.pageNumber - 1; +function webViewerTextLayerRendered(e) { + var pageIndex = e.pageNumber - 1; var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex); //#if !PRODUCTION @@ -1553,14 +1588,14 @@ document.addEventListener('textlayerrendered', function (e) { PDFViewerApplication.fallback(); } //#endif -}, true); +} -document.addEventListener('pagemode', function (evt) { +function webViewerPageMode(e) { if (!PDFViewerApplication.initialized) { return; } // Handle the 'pagemode' hash parameter, see also `PDFLinkService_setHash`. - var mode = evt.detail.mode, view; + var mode = e.mode, view; switch (mode) { case 'thumbs': view = SidebarView.THUMBS; @@ -1580,15 +1615,15 @@ document.addEventListener('pagemode', function (evt) { return; } PDFViewerApplication.pdfSidebar.switchView(view, /* forceOpen = */ true); -}, true); +} -document.addEventListener('namedaction', function (e) { +function webViewerNamedAction(e) { if (!PDFViewerApplication.initialized) { return; } // Processing couple of named actions that might be useful. // See also PDFLinkService.executeNamedAction - var action = e.detail.action; + var action = e.action; switch (action) { case 'GoToPage': PDFViewerApplication.appConfig.toolbar.pageNumber.focus(); @@ -1600,17 +1635,17 @@ document.addEventListener('namedaction', function (e) { } break; } -}, true); +} -window.addEventListener('presentationmodechanged', function (e) { - var active = e.detail.active; - var switchInProgress = e.detail.switchInProgress; +function webViewerPresentationModeChanged(e) { + var active = e.active; + var switchInProgress = e.switchInProgress; PDFViewerApplication.pdfViewer.presentationModeState = switchInProgress ? PresentationModeState.CHANGING : active ? PresentationModeState.FULLSCREEN : PresentationModeState.NORMAL; -}); +} -window.addEventListener('sidebarviewchanged', function (evt) { +function webViewerSidebarViewChanged(e) { if (!PDFViewerApplication.initialized) { return; } @@ -1623,15 +1658,15 @@ window.addEventListener('sidebarviewchanged', function (evt) { return; } store.initializedPromise.then(function() { - store.set('sidebarView', evt.detail.view).catch(function() {}); + store.set('sidebarView', e.view).catch(function() {}); }); -}, true); +} -window.addEventListener('updateviewarea', function (evt) { +function webViewerUpdateViewarea(e) { if (!PDFViewerApplication.initialized) { return; } - var location = evt.location, store = PDFViewerApplication.store; + var location = e.location, store = PDFViewerApplication.store; if (store) { store.initializedPromise.then(function() { @@ -1663,9 +1698,13 @@ window.addEventListener('updateviewarea', function (evt) { } else { pageNumberInput.classList.add(PAGE_NUMBER_LOADING_INDICATOR); } -}, true); +} window.addEventListener('resize', function webViewerResize(evt) { + PDFViewerApplication.eventBus.dispatch('resize'); +}); + +function webViewerResize() { if (PDFViewerApplication.initialized) { var currentScaleValue = PDFViewerApplication.pdfViewer.currentScaleValue; if (currentScaleValue === 'auto' || @@ -1684,11 +1723,16 @@ window.addEventListener('resize', function webViewerResize(evt) { // Set the 'max-height' CSS property of the secondary toolbar. SecondaryToolbar.setMaxHeight(PDFViewerApplication.appConfig.mainContainer); -}); +} window.addEventListener('hashchange', function webViewerHashchange(evt) { + var hash = document.location.hash.substring(1); + PDFViewerApplication.eventBus.dispatch('hashchange', {hash: hash}); +}); + +function webViewerHashchange(e) { if (PDFViewerApplication.pdfHistory.isHashChangeUnlocked) { - var hash = document.location.hash.substring(1); + var hash = e.hash; if (!hash) { return; } @@ -1698,7 +1742,7 @@ window.addEventListener('hashchange', function webViewerHashchange(evt) { PDFViewerApplication.pdfLinkService.setHash(hash); } } -}); +} //#if GENERIC window.addEventListener('change', function webViewerChange(evt) { @@ -1706,7 +1750,12 @@ window.addEventListener('change', function webViewerChange(evt) { if (!files || files.length === 0) { return; } - var file = files[0]; + PDFViewerApplication.eventBus.dispatch('fileinputchange', + {fileInput: evt.target}); +}, true); + +function webViewerFileInputChange(e) { + var file = e.fileInput.files[0]; if (!pdfjsLib.PDFJS.disableCreateObjectURL && typeof URL !== 'undefined' && URL.createObjectURL) { @@ -1730,7 +1779,7 @@ window.addEventListener('change', function webViewerChange(evt) { appConfig.secondaryToolbar.viewBookmark.setAttribute('hidden', 'true'); appConfig.toolbar.download.setAttribute('hidden', 'true'); appConfig.secondaryToolbar.download.setAttribute('hidden', 'true'); -}, true); +} //#endif function selectScaleOption(value) { @@ -1749,6 +1798,10 @@ function selectScaleOption(value) { } window.addEventListener('localized', function localized(evt) { + PDFViewerApplication.eventBus.dispatch('localized'); +}); + +function webViewerLocalized() { document.getElementsByTagName('html')[0].dir = mozL10n.getDirection(); PDFViewerApplication.animationStartedPromise.then(function() { @@ -1772,19 +1825,19 @@ window.addEventListener('localized', function localized(evt) { // Set the 'max-height' CSS property of the secondary toolbar. SecondaryToolbar.setMaxHeight(PDFViewerApplication.appConfig.mainContainer); }); -}, true); +} -window.addEventListener('scalechange', function scalechange(evt) { +function webViewerScaleChange(e) { var appConfig = PDFViewerApplication.appConfig; - appConfig.toolbar.zoomOut.disabled = (evt.scale === MIN_SCALE); - appConfig.toolbar.zoomIn.disabled = (evt.scale === MAX_SCALE); + appConfig.toolbar.zoomOut.disabled = (e.scale === MIN_SCALE); + appConfig.toolbar.zoomIn.disabled = (e.scale === MAX_SCALE); // Update the 'scaleSelect' DOM element. - var predefinedValueFound = selectScaleOption(evt.presetValue || - '' + evt.scale); + var predefinedValueFound = selectScaleOption(e.presetValue || + '' + e.scale); if (!predefinedValueFound) { var customScaleOption = appConfig.toolbar.customScaleOption; - var customScale = Math.round(evt.scale * 10000) / 100; + var customScale = Math.round(e.scale * 10000) / 100; customScaleOption.textContent = mozL10n.get('page_scale_percent', { scale: customScale }, '{{scale}}%'); customScaleOption.selected = true; @@ -1793,11 +1846,11 @@ window.addEventListener('scalechange', function scalechange(evt) { return; } PDFViewerApplication.pdfViewer.update(); -}, true); +} -window.addEventListener('pagechange', function pagechange(evt) { - var page = evt.pageNumber; - if (evt.previousPageNumber !== page) { +function webViewerPageChange(e) { + var page = e.pageNumber; + if (e.previousPageNumber !== page) { PDFViewerApplication.appConfig.toolbar.pageNumber.value = page; if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) { @@ -1819,7 +1872,7 @@ window.addEventListener('pagechange', function pagechange(evt) { Stats.add(page, pageView.stats); } } -}, true); +} var zoomDisabled = false, zoomDisabledTimeout; function handleMouseWheel(evt) { @@ -1908,8 +1961,8 @@ window.addEventListener('keydown', function keydown(evt) { break; case 71: // g if (!PDFViewerApplication.supportsIntegratedFind) { - PDFViewerApplication.findBar.dispatchEvent('again', - cmd === 5 || cmd === 12); + PDFViewerApplication.eventBus.dispatch('findagain', + cmd === 5 || cmd === 12); handled = true; } break; @@ -2127,11 +2180,11 @@ window.addEventListener('keydown', function keydown(evt) { }); window.addEventListener('beforeprint', function beforePrint(evt) { - PDFViewerApplication.beforePrint(); + PDFViewerApplication.eventBus.dispatch('beforeprint'); }); window.addEventListener('afterprint', function afterPrint(evt) { - PDFViewerApplication.afterPrint(); + PDFViewerApplication.eventBus.dispatch('afterprint'); }); (function animationStartedClosure() { diff --git a/web/dom_events.js b/web/dom_events.js new file mode 100644 index 000000000..03d7e9874 --- /dev/null +++ b/web/dom_events.js @@ -0,0 +1,152 @@ +/* Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define('pdfjs-web/dom_events', ['exports', 'pdfjs-web/ui_utils'], factory); + } else if (typeof exports !== 'undefined') { + factory(exports, require('./ui_utils.js')); + } else { + factory((root.pdfjsWebDOMEvents = {}), root.pdfjsWebUIUtils); + } +}(this, function (exports, uiUtils) { + var EventBus = uiUtils.EventBus; + + // Attaching to the application event bus to dispatch events to the DOM for + // backwards viewer API compatibility. + function attachDOMEventsToEventBus(eventBus) { + eventBus.on('documentload', function () { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('documentload', true, true, {}); + window.dispatchEvent(event); + }); + eventBus.on('pagerendered', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagerendered', true, true, { + pageNumber: e.pageNumber, + cssTransform: e.cssTransform, + }); + e.source.div.dispatchEvent(event); + }); + eventBus.on('textlayerrendered', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('textlayerrendered', true, true, { + pageNumber: e.pageNumber + }); + e.source.textLayerDiv.dispatchEvent(event); + }); + eventBus.on('pagechange', function (e) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', true, true, window, 0); + event.updateInProgress = e.updateInProgress; + event.pageNumber = e.pageNumber; + event.previousPageNumber = e.previousPageNumber; + e.source.container.dispatchEvent(event); + }); + eventBus.on('pagesinit', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagesinit', true, true, null); + e.source.container.dispatchEvent(event); + }); + eventBus.on('pagesloaded', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagesloaded', true, true, { + pagesCount: e.pagesCount + }); + e.source.container.dispatchEvent(event); + }); + eventBus.on('scalechange', function (e) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('scalechange', true, true, window, 0); + event.scale = e.scale; + event.presetValue = e.presetValue; + e.source.container.dispatchEvent(event); + }); + eventBus.on('updateviewarea', function (e) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('updateviewarea', true, true, window, 0); + event.location = e.location; + e.source.container.dispatchEvent(event); + }); + eventBus.on('find', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('find' + e.type, true, true, { + query: e.query, + caseSensitive: e.caseSensitive, + highlightAll: e.highlightAll, + findPrevious: e.findPrevious + }); + window.dispatchEvent(event); + }); + eventBus.on('attachmentsloaded', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('attachmentsloaded', true, true, { + attachmentsCount: e.attachmentsCount + }); + e.source.container.dispatchEvent(event); + }); + eventBus.on('sidebarviewchanged', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('sidebarviewchanged', true, true, { + view: e.view, + }); + e.source.outerContainer.dispatchEvent(event); + }); + eventBus.on('pagemode', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('pagemode', true, true, { + mode: e.mode, + }); + e.source.pdfViewer.container.dispatchEvent(event); + }); + eventBus.on('namedaction', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('namedaction', true, true, { + action: e.action + }); + e.source.pdfViewer.container.dispatchEvent(event); + }); + eventBus.on('presentationmodechanged', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('presentationmodechanged', true, true, { + active: e.active, + switchInProgress: e.switchInProgress + }); + window.dispatchEvent(event); + }); + eventBus.on('outlineloaded', function (e) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('outlineloaded', true, true, { + outlineCount: e.outlineCount + }); + e.source.container.dispatchEvent(event); + }); + } + + var globalEventBus = null; + function getGlobalEventBus() { + if (globalEventBus) { + return globalEventBus; + } + globalEventBus = new EventBus(); + attachDOMEventsToEventBus(globalEventBus); + return globalEventBus; + } + + exports.attachDOMEventsToEventBus = attachDOMEventsToEventBus; + exports.getGlobalEventBus = getGlobalEventBus; +})); diff --git a/web/hand_tool.js b/web/hand_tool.js index 8d5f1842a..c6690884d 100644 --- a/web/hand_tool.js +++ b/web/hand_tool.js @@ -40,6 +40,7 @@ var SecondaryToolbar = secondaryToolbar.SecondaryToolbar; * @property {HTMLDivElement} container - The document container. * @property {HTMLButtonElement} toggleHandTool - The button element for * toggling the hand tool. + * @property {EventBus} eventBus - The application event bus. */ /** @@ -52,6 +53,7 @@ var HandTool = (function HandToolClosure() { */ function HandTool(options) { this.container = options.container; + this.eventBus = options.eventBus; this.toggleHandTool = options.toggleHandTool; this.wasActive = false; @@ -79,7 +81,7 @@ var HandTool = (function HandToolClosure() { if (this.toggleHandTool) { this.toggleHandTool.addEventListener('click', this.toggle.bind(this)); - window.addEventListener('localized', function (evt) { + this.eventBus.on('localized', function (e) { Preferences.get('enableHandToolOnLoad').then(function resolved(value) { if (value) { this.handTool.activate(); @@ -87,11 +89,11 @@ var HandTool = (function HandToolClosure() { }.bind(this), function rejected(reason) {}); }.bind(this)); - window.addEventListener('presentationmodechanged', function (evt) { - if (evt.detail.switchInProgress) { + this.eventBus.on('presentationmodechanged', function (e) { + if (e.switchInProgress) { return; } - if (evt.detail.active) { + if (e.active) { this.enterPresentationMode(); } else { this.exitPresentationMode(); diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index 5d29d1faf..2f171785e 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -29,6 +29,7 @@ /** * @typedef {Object} PDFAttachmentViewerOptions * @property {HTMLDivElement} container - The viewer element. + * @property {EventBus} eventBus - The application event bus. * @property {DownloadManager} downloadManager - The download manager. */ @@ -48,6 +49,7 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { function PDFAttachmentViewer(options) { this.attachments = null; this.container = options.container; + this.eventBus = options.eventBus; this.downloadManager = options.downloadManager; } @@ -66,11 +68,10 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { */ _dispatchEvent: function PDFAttachmentViewer_dispatchEvent(attachmentsCount) { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('attachmentsloaded', true, true, { + this.eventBus.dispatch('attachmentsloaded', { + source: this, attachmentsCount: attachmentsCount }); - this.container.dispatchEvent(event); }, /** diff --git a/web/pdf_find_bar.js b/web/pdf_find_bar.js index 02d2e75f4..bdcd0a5cf 100644 --- a/web/pdf_find_bar.js +++ b/web/pdf_find_bar.js @@ -51,6 +51,7 @@ var PDFFindBar = (function PDFFindBarClosure() { this.findPreviousButton = options.findPreviousButton || null; this.findNextButton = options.findNextButton || null; this.findController = options.findController || null; + this.eventBus = options.eventBus; if (this.findController === null) { throw new Error('PDFFindBar cannot be used without a ' + @@ -103,14 +104,14 @@ var PDFFindBar = (function PDFFindBarClosure() { }, dispatchEvent: function PDFFindBar_dispatchEvent(type, findPrev) { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('find' + type, true, true, { + this.eventBus.dispatch('find', { + source: this, + type: type, query: this.findField.value, caseSensitive: this.caseSensitive.checked, highlightAll: this.highlightAll.checked, findPrevious: findPrev }); - return window.dispatchEvent(event); }, updateUIState: diff --git a/web/pdf_history.js b/web/pdf_history.js index 403caef39..d60a2f4f5 100644 --- a/web/pdf_history.js +++ b/web/pdf_history.js @@ -17,16 +17,18 @@ (function (root, factory) { if (typeof define === 'function' && define.amd) { - define('pdfjs-web/pdf_history', ['exports'], factory); + define('pdfjs-web/pdf_history', ['exports', 'pdfjs-web/dom_events'], + factory); } else if (typeof exports !== 'undefined') { - factory(exports); + factory(exports, require('./dom_events.js')); } else { - factory((root.pdfjsWebPDFHistory = {})); + factory((root.pdfjsWebPDFHistory = {}), root.pdfjsWebDOMEvents); } -}(this, function (exports) { +}(this, function (exports, domEvents) { function PDFHistory(options) { this.linkService = options.linkService; + this.eventBus = options.eventBus || domEvents.getGlobalEventBus(); this.initialized = false; this.initialDestination = null; @@ -173,8 +175,8 @@ window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false); }, false); - window.addEventListener('presentationmodechanged', function(e) { - self.isViewerInPresentationMode = !!e.detail.active; + self.eventBus.on('presentationmodechanged', function(e) { + self.isViewerInPresentationMode = e.active; }); }, diff --git a/web/pdf_link_service.js b/web/pdf_link_service.js index eac27ddf3..9228dd914 100644 --- a/web/pdf_link_service.js +++ b/web/pdf_link_service.js @@ -17,17 +17,23 @@ (function (root, factory) { if (typeof define === 'function' && define.amd) { - define('pdfjs-web/pdf_link_service', ['exports', 'pdfjs-web/ui_utils'], - factory); + define('pdfjs-web/pdf_link_service', ['exports', 'pdfjs-web/ui_utils', + 'pdfjs-web/dom_events'], factory); } else if (typeof exports !== 'undefined') { - factory(exports, require('./ui_utils.js')); + factory(exports, require('./ui_utils.js'), require('./dom_events.js')); } else { - factory((root.pdfjsWebPDFLinkService = {}), root.pdfjsWebUIUtils); + factory((root.pdfjsWebPDFLinkService = {}), root.pdfjsWebUIUtils, + root.pdfjsWebDOMEvents); } -}(this, function (exports, uiUtils) { +}(this, function (exports, uiUtils, domEvents) { var parseQueryString = uiUtils.parseQueryString; +/** + * @typedef {Object} PDFLinkServiceOptions + * @property {EventBus} eventBus - The application event bus. + */ + /** * Performs navigation functions inside PDF, such as opening specified page, * or destination. @@ -37,8 +43,11 @@ var parseQueryString = uiUtils.parseQueryString; var PDFLinkService = (function () { /** * @constructs PDFLinkService + * @param {PDFLinkServiceOptions} options */ - function PDFLinkService() { + function PDFLinkService(options) { + options = options || {}; + this.eventBus = options.eventBus || domEvents.getGlobalEventBus(); this.baseUrl = null; this.pdfDocument = null; this.pdfViewer = null; @@ -238,11 +247,10 @@ var PDFLinkService = (function () { this.page = pageNumber; // simple page } if ('pagemode' in params) { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagemode', true, true, { - mode: params.pagemode, + this.eventBus.dispatch('pagemode', { + source: this, + mode: params.pagemode }); - this.pdfViewer.container.dispatchEvent(event); } } else if (/^\d+$/.test(hash)) { // page number this.page = hash; @@ -292,11 +300,10 @@ var PDFLinkService = (function () { break; // No action according to spec } - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('namedaction', true, true, { + this.eventBus.dispatch('namedaction', { + source: this, action: action }); - this.pdfViewer.container.dispatchEvent(event); }, /** diff --git a/web/pdf_outline_viewer.js b/web/pdf_outline_viewer.js index c2310881a..6d9345963 100644 --- a/web/pdf_outline_viewer.js +++ b/web/pdf_outline_viewer.js @@ -32,6 +32,7 @@ var DEFAULT_TITLE = '\u2013'; * @typedef {Object} PDFOutlineViewerOptions * @property {HTMLDivElement} container - The viewer element. * @property {IPDFLinkService} linkService - The navigation/linking service. + * @property {EventBus} eventBus - The application event bus. */ /** @@ -52,6 +53,7 @@ var PDFOutlineViewer = (function PDFOutlineViewerClosure() { this.lastToggleIsShow = true; this.container = options.container; this.linkService = options.linkService; + this.eventBus = options.eventBus; } PDFOutlineViewer.prototype = { @@ -69,11 +71,10 @@ var PDFOutlineViewer = (function PDFOutlineViewerClosure() { * @private */ _dispatchEvent: function PDFOutlineViewer_dispatchEvent(outlineCount) { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('outlineloaded', true, true, { + this.eventBus.dispatch('outlineloaded', { + source: this, outlineCount: outlineCount }); - this.container.dispatchEvent(event); }, /** diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 140184d9b..87f1ceacb 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -19,15 +19,17 @@ if (typeof define === 'function' && define.amd) { define('pdfjs-web/pdf_page_view', ['exports', 'pdfjs-web/ui_utils', 'pdfjs-web/pdf_rendering_queue', - 'pdfjs-web/pdfjs'], factory); + 'pdfjs-web/dom_events', 'pdfjs-web/pdfjs'], factory); } else if (typeof exports !== 'undefined') { factory(exports, require('./ui_utils.js'), - require('./pdf_rendering_queue.js'), require('./pdfjs.js')); + require('./pdf_rendering_queue.js'), require('./dom_events.js'), + require('./pdfjs.js')); } else { factory((root.pdfjsWebPDFPageView = {}), root.pdfjsWebUIUtils, - root.pdfjsWebPDFRenderingQueue, root.pdfjsWebPDFJS); + root.pdfjsWebPDFRenderingQueue, root.pdfjsWebDOMEvents, + root.pdfjsWebPDFJS); } -}(this, function (exports, uiUtils, pdfRenderingQueue, pdfjsLib) { +}(this, function (exports, uiUtils, pdfRenderingQueue, domEvents, pdfjsLib) { var CSS_UNITS = uiUtils.CSS_UNITS; var DEFAULT_SCALE = uiUtils.DEFAULT_SCALE; @@ -41,6 +43,7 @@ var TEXT_LAYER_RENDER_DELAY = 200; // ms /** * @typedef {Object} PDFPageViewOptions * @property {HTMLDivElement} container - The viewer element. + * @property {EventBus} eventBus - The application event bus. * @property {number} id - The page unique ID (normally its number). * @property {number} scale - The page scale display. * @property {PageViewport} defaultViewport - The page viewport. @@ -76,6 +79,7 @@ var PDFPageView = (function PDFPageViewClosure() { this.pdfPageRotate = defaultViewport.rotation; this.hasRestrictedScaling = false; + this.eventBus = options.eventBus || domEvents.getGlobalEventBus(); this.renderingQueue = renderingQueue; this.textLayerFactory = textLayerFactory; this.annotationLayerFactory = annotationLayerFactory; @@ -196,13 +200,11 @@ var PDFPageView = (function PDFPageViewClosure() { (this.hasRestrictedScaling && isScalingRestricted)) { this.cssTransform(this.canvas, true); - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagerendered', true, true, { + this.eventBus.dispatch('pagerendered', { + source: this, pageNumber: this.id, cssTransform: true, }); - this.div.dispatchEvent(event); - return; } if (!this.zoomLayer) { @@ -450,12 +452,11 @@ var PDFPageView = (function PDFPageViewClosure() { if (self.onAfterDraw) { self.onAfterDraw(); } - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagerendered', true, true, { + self.eventBus.dispatch('pagerendered', { + source: self, pageNumber: self.id, cssTransform: false, }); - div.dispatchEvent(event); if (!error) { resolveRenderPromise(undefined); diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index 537ee3dbe..d7df216e3 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -35,6 +35,7 @@ var CONTROLS_SELECTOR = 'pdfPresentationModeControls'; * @property {HTMLDivElement} container - The container for the viewer element. * @property {HTMLDivElement} viewer - (optional) The viewer element. * @property {PDFViewer} pdfViewer - The document viewer. + * @property {EventBus} eventBus - The application event bus. * @property {Array} contextMenuItems - (optional) The menuitems that are added * to the context menu in Presentation Mode. */ @@ -51,6 +52,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; this.pdfViewer = options.pdfViewer; + this.eventBus = options.eventBus; var contextMenuItems = options.contextMenuItems || null; this.active = false; @@ -163,12 +165,11 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { * @private */ _notifyStateChange: function PDFPresentationMode_notifyStateChange() { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('presentationmodechanged', true, true, { + this.eventBus.dispatch('presentationmodechanged', { + source: this, active: this.active, switchInProgress: !!this.switchInProgress }); - window.dispatchEvent(event); }, /** diff --git a/web/pdf_sidebar.js b/web/pdf_sidebar.js index 0cbae2c50..8d9472950 100644 --- a/web/pdf_sidebar.js +++ b/web/pdf_sidebar.js @@ -44,6 +44,7 @@ var SidebarView = { * (in which the viewer element is placed). * @property {HTMLDivElement} outerContainer - The outer container * (encasing both the viewer and sidebar elements). + * @property {EventBus} eventBus - The application event bus. * @property {HTMLButtonElement} toggleButton - The button used for * opening/closing the sidebar. * @property {HTMLButtonElement} thumbnailButton - The button used to show @@ -85,6 +86,7 @@ var PDFSidebar = (function PDFSidebarClosure() { this.mainContainer = options.mainContainer; this.outerContainer = options.outerContainer; + this.eventBus = options.eventBus; this.toggleButton = options.toggleButton; this.thumbnailButton = options.thumbnailButton; @@ -272,11 +274,10 @@ var PDFSidebar = (function PDFSidebarClosure() { * @private */ _dispatchEvent: function PDFSidebar_dispatchEvent() { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('sidebarviewchanged', true, true, { - view: this.visibleView, + this.eventBus.dispatch('sidebarviewchanged', { + source: this, + view: this.visibleView }); - this.outerContainer.dispatchEvent(event); }, /** @@ -339,8 +340,8 @@ var PDFSidebar = (function PDFSidebarClosure() { }); // Disable/enable views. - self.outlineView.addEventListener('outlineloaded', function(evt) { - var outlineCount = evt.detail.outlineCount; + self.eventBus.on('outlineloaded', function(e) { + var outlineCount = e.outlineCount; self.outlineButton.disabled = !outlineCount; if (!outlineCount && self.active === SidebarView.OUTLINE) { @@ -348,8 +349,8 @@ var PDFSidebar = (function PDFSidebarClosure() { } }); - self.attachmentsView.addEventListener('attachmentsloaded', function(evt) { - var attachmentsCount = evt.detail.attachmentsCount; + self.eventBus.on('attachmentsloaded', function(e) { + var attachmentsCount = e.attachmentsCount; self.attachmentsButton.disabled = !attachmentsCount; if (!attachmentsCount && self.active === SidebarView.ATTACHMENTS) { @@ -358,9 +359,8 @@ var PDFSidebar = (function PDFSidebarClosure() { }); // Update the thumbnailViewer, if visible, when exiting presentation mode. - window.addEventListener('presentationmodechanged', function(evt) { - if (!evt.detail.active && !evt.detail.switchInProgress && - self.isThumbnailViewVisible) { + self.eventBus.on('presentationmodechanged', function(e) { + if (!e.active && !e.switchInProgress && self.isThumbnailViewVisible) { self._updateThumbnailViewer(); } }); diff --git a/web/pdf_viewer.component.js b/web/pdf_viewer.component.js index 9f4cd129c..fe7481737 100644 --- a/web/pdf_viewer.component.js +++ b/web/pdf_viewer.component.js @@ -52,6 +52,7 @@ PDFJS.PDFHistory = pdfViewerLibs.pdfjsWebPDFHistory.PDFHistory; PDFJS.PDFFindController = pdfViewerLibs.pdfjsWebPDFFindController.PDFFindController; + PDFJS.EventBus = pdfViewerLibs.pdfjsWebUIUtils.EventBus; PDFJS.DownloadManager = pdfViewerLibs.pdfjsWebDownloadManager.DownloadManager; PDFJS.ProgressBar = pdfViewerLibs.pdfjsWebUIUtils.ProgressBar; diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index d932867c6..5bf870388 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -20,21 +20,23 @@ define('pdfjs-web/pdf_viewer', ['exports', 'pdfjs-web/ui_utils', 'pdfjs-web/pdf_page_view', 'pdfjs-web/pdf_rendering_queue', 'pdfjs-web/text_layer_builder', 'pdfjs-web/annotation_layer_builder', - 'pdfjs-web/pdf_link_service', 'pdfjs-web/pdfjs'], factory); + 'pdfjs-web/pdf_link_service', 'pdfjs-web/dom_events', 'pdfjs-web/pdfjs'], + factory); } else if (typeof exports !== 'undefined') { factory(exports, require('./ui_utils.js'), require('./pdf_page_view.js'), require('./pdf_rendering_queue.js'), require('./text_layer_builder.js'), require('./annotation_layer_builder.js'), - require('./pdf_link_service.js'), require('./pdfjs.js')); + require('./pdf_link_service.js'), require('./dom_events.js'), + require('./pdfjs.js')); } else { factory((root.pdfjsWebPDFViewer = {}), root.pdfjsWebUIUtils, root.pdfjsWebPDFPageView, root.pdfjsWebPDFRenderingQueue, root.pdfjsWebTextLayerBuilder, root.pdfjsWebAnnotationLayerBuilder, - root.pdfjsWebPDFLinkService, root.pdfjsWebPDFJS); + root.pdfjsWebPDFLinkService, root.pdfjsWebDOMEvents, root.pdfjsWebPDFJS); } }(this, function (exports, uiUtils, pdfPageView, pdfRenderingQueue, textLayerBuilder, annotationLayerBuilder, pdfLinkService, - pdfjsLib) { + domEvents, pdfjsLib) { var UNKNOWN_SCALE = uiUtils.UNKNOWN_SCALE; var SCROLLBAR_PADDING = uiUtils.SCROLLBAR_PADDING; @@ -66,6 +68,7 @@ var DEFAULT_CACHE_SIZE = 10; * @typedef {Object} PDFViewerOptions * @property {HTMLDivElement} container - The container for the viewer element. * @property {HTMLDivElement} viewer - (optional) The viewer element. + * @property {EventBus} eventBus - The application event bus. * @property {IPDFLinkService} linkService - The navigation/linking service. * @property {DownloadManager} downloadManager - (optional) The download * manager component. @@ -120,6 +123,7 @@ var PDFViewer = (function pdfViewer() { function PDFViewer(options) { this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; + this.eventBus = options.eventBus || domEvents.getGlobalEventBus(); this.linkService = options.linkService || new SimpleLinkService(); this.downloadManager = options.downloadManager || null; this.removePageBorders = options.removePageBorders || false; @@ -162,21 +166,23 @@ var PDFViewer = (function pdfViewer() { return; } - var event = document.createEvent('UIEvents'); - event.initUIEvent('pagechange', true, true, window, 0); - event.updateInProgress = this.updateInProgress; - if (!(0 < val && val <= this.pagesCount)) { - event.pageNumber = this._currentPageNumber; - event.previousPageNumber = val; - this.container.dispatchEvent(event); + this.eventBus.dispatch('pagechange', { + source: this, + updateInProgress: this.updateInProgress, + pageNumber: this._currentPageNumber, + previousPageNumber: val + }); return; } - event.previousPageNumber = this._currentPageNumber; + this.eventBus.dispatch('pagechange', { + source: this, + updateInProgress: this.updateInProgress, + pageNumber: val, + previousPageNumber: this._currentPageNumber + }); this._currentPageNumber = val; - event.pageNumber = val; - this.container.dispatchEvent(event); // Check if the caller is `PDFViewer_update`, to avoid breaking scrolling. if (this.updateInProgress) { @@ -274,11 +280,10 @@ var PDFViewer = (function pdfViewer() { }); this.pagesPromise = pagesPromise; pagesPromise.then(function () { - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagesloaded', true, true, { + self.eventBus.dispatch('pagesloaded', { + source: self, pagesCount: pagesCount }); - self.container.dispatchEvent(event); }); var isOnePageRenderedResolved = false; @@ -319,6 +324,7 @@ var PDFViewer = (function pdfViewer() { } var pageView = new PDFPageView({ container: this.viewer, + eventBus: this.eventBus, id: pageNum, scale: scale, defaultViewport: viewport.clone(), @@ -357,9 +363,7 @@ var PDFViewer = (function pdfViewer() { } }); - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('pagesinit', true, true, null); - self.container.dispatchEvent(event); + self.eventBus.dispatch('pagesinit', {source: self}); if (this.defaultRenderingQueue) { this.update(); @@ -399,13 +403,11 @@ var PDFViewer = (function pdfViewer() { _setScaleDispatchEvent: function pdfViewer_setScaleDispatchEvent( newScale, newValue, preset) { - var event = document.createEvent('UIEvents'); - event.initUIEvent('scalechange', true, true, window, 0); - event.scale = newScale; - if (preset) { - event.presetValue = newValue; - } - this.container.dispatchEvent(event); + this.eventBus.dispatch('scalechange', { + source: this, + scale: newScale, + presetValue: preset ? newValue : undefined + }); }, _setScaleUpdatePages: function pdfViewer_setScaleUpdatePages( @@ -671,10 +673,10 @@ var PDFViewer = (function pdfViewer() { this.updateInProgress = false; - var event = document.createEvent('UIEvents'); - event.initUIEvent('updateviewarea', true, true, window, 0); - event.location = this._location; - this.container.dispatchEvent(event); + this.eventBus.dispatch('updateviewarea', { + source: this, + location: this._location + }); }, containsElement: function (element) { @@ -772,6 +774,7 @@ var PDFViewer = (function pdfViewer() { createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) { return new TextLayerBuilder({ textLayerDiv: textLayerDiv, + eventBus: this.eventBus, pageIndex: pageIndex, viewport: viewport, findController: this.isInPresentationMode ? null : this.findController diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index 5d6b1429f..98abed00b 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -17,18 +17,21 @@ (function (root, factory) { if (typeof define === 'function' && define.amd) { - define('pdfjs-web/text_layer_builder', ['exports', 'pdfjs-web/pdfjs'], + define('pdfjs-web/text_layer_builder', ['exports', 'pdfjs-web/dom_events', + 'pdfjs-web/pdfjs'], factory); } else if (typeof exports !== 'undefined') { - factory(exports, require('./pdfjs.js')); + factory(exports, require('./dom_events.js'), require('./pdfjs.js')); } else { - factory((root.pdfjsWebTextLayerBuilder = {}), root.pdfjsWebPDFJS); + factory((root.pdfjsWebTextLayerBuilder = {}), root.pdfjsWebDOMEvents, + root.pdfjsWebPDFJS); } -}(this, function (exports, pdfjsLib) { +}(this, function (exports, domEvents, pdfjsLib) { /** * @typedef {Object} TextLayerBuilderOptions * @property {HTMLDivElement} textLayerDiv - The text layer container. + * @property {EventBus} eventBus - The application event bus. * @property {number} pageIndex - The page index. * @property {PageViewport} viewport - The viewport of the text layer. * @property {PDFFindController} findController @@ -44,6 +47,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { function TextLayerBuilder(options) { this.textLayerDiv = options.textLayerDiv; + this.eventBus = options.eventBus || domEvents.getGlobalEventBus(); this.renderingDone = false; this.divContentDone = false; this.pageIdx = options.pageIndex; @@ -64,11 +68,10 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { endOfContent.className = 'endOfContent'; this.textLayerDiv.appendChild(endOfContent); - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('textlayerrendered', true, true, { + this.eventBus.dispatch('textlayerrendered', { + source: this, pageNumber: this.pageNumber }); - this.textLayerDiv.dispatchEvent(event); }, /** diff --git a/web/ui_utils.js b/web/ui_utils.js index 090440206..3aba18154 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -384,6 +384,49 @@ function getPDFFileNameFromURL(url) { return suggestedFilename || 'document.pdf'; } +/** + * 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. + */ +var EventBus = (function EventBusClosure() { + function EventBus() { + this._listeners = Object.create(null); + } + EventBus.prototype = { + on: function EventBus_on(eventName, listener) { + var eventListeners = this._listeners[eventName]; + if (!eventListeners) { + eventListeners = []; + this._listeners[eventName] = eventListeners; + } + eventListeners.push(listener); + }, + off: function EventBus_on(eventName, listener) { + var eventListeners = this._listeners[eventName]; + var i; + if (!eventListeners || ((i = eventListeners.indexOf(listener)) < 0)) { + return; + } + eventListeners.splice(i, 1); + }, + dispatch: function EventBus_dispath(eventName) { + var eventListeners = this._listeners[eventName]; + if (!eventListeners || eventListeners.length === 0) { + return; + } + // Passing all arguments after the eventName to the listeners. + var args = Array.prototype.slice.call(arguments, 1); + // Making copy of the listeners array in case if it will be modified + // during dispatch. + eventListeners.slice(0).forEach(function (listener) { + listener.apply(null, args); + }); + } + }; + return EventBus; +})(); + var ProgressBar = (function ProgressBarClosure() { function clamp(v, min, max) { @@ -474,6 +517,7 @@ exports.MAX_AUTO_SCALE = MAX_AUTO_SCALE; exports.SCROLLBAR_PADDING = SCROLLBAR_PADDING; exports.VERTICAL_PADDING = VERTICAL_PADDING; exports.mozL10n = mozL10n; +exports.EventBus = EventBus; exports.ProgressBar = ProgressBar; exports.getPDFFileNameFromURL = getPDFFileNameFromURL; exports.noContextMenuHandler = noContextMenuHandler; diff --git a/web/viewer.js b/web/viewer.js index a6bb8d3a3..245e64219 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -55,6 +55,7 @@ function getViewerConfiguration() { appContainer: document.body, mainContainer: document.getElementById('viewerContainer'), viewerContainer: document.getElementById('viewer'), + eventBus: null, // using global event bus with DOM events toolbar: { numPages: document.getElementById('numPages'), pageNumber: document.getElementById('pageNumber'), From 3e6e294fd41971a856c5c57d64afabc3b1d152da Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Thu, 28 Apr 2016 07:11:40 -0500 Subject: [PATCH 2/2] Refactors PDFFindBar and FirefoxCom find events. --- web/app.js | 22 +++++++++++++++++++--- web/dom_events.js | 3 +++ web/firefoxcom.js | 26 ++++++++++++++++++++++++++ web/pdf_find_controller.js | 16 ---------------- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/web/app.js b/web/app.js index e2d1b9cce..ebafbcf53 100644 --- a/web/app.js +++ b/web/app.js @@ -248,7 +248,6 @@ var PDFViewerApplication = { this.findBar.updateUIState(state, previous, matchCount); } }.bind(this); - this.findController.listenWindowEvents(); this.pdfViewer.setFindController(this.findController); @@ -1249,6 +1248,7 @@ var PDFViewerApplication = { eventBus.on('pagemode', webViewerPageMode); eventBus.on('namedaction', webViewerNamedAction); eventBus.on('presentationmodechanged', webViewerPresentationModeChanged); + eventBus.on('find', webViewerFind); //#if GENERIC eventBus.on('fileinputchange', webViewerFileInputChange); //#endif @@ -1827,6 +1827,15 @@ function webViewerLocalized() { }); } +function webViewerFind(e) { + PDFViewerApplication.findController.executeCommand('find' + e.type, { + query: e.query, + caseSensitive: e.caseSensitive, + highlightAll: e.highlightAll, + findPrevious: e.findPrevious + }); +} + function webViewerScaleChange(e) { var appConfig = PDFViewerApplication.appConfig; appConfig.toolbar.zoomOut.disabled = (e.scale === MIN_SCALE); @@ -1961,8 +1970,15 @@ window.addEventListener('keydown', function keydown(evt) { break; case 71: // g if (!PDFViewerApplication.supportsIntegratedFind) { - PDFViewerApplication.eventBus.dispatch('findagain', - cmd === 5 || cmd === 12); + var findState = PDFViewerApplication.findController.state; + if (findState) { + PDFViewerApplication.findController.executeCommand('findagain', { + query: findState.query, + caseSensitive: findState.caseSensitive, + highlightAll: findState.highlightAll, + findPrevious: cmd === 5 || cmd === 12 + }); + } handled = true; } break; diff --git a/web/dom_events.js b/web/dom_events.js index 03d7e9874..64c190005 100644 --- a/web/dom_events.js +++ b/web/dom_events.js @@ -83,6 +83,9 @@ e.source.container.dispatchEvent(event); }); eventBus.on('find', function (e) { + if (e.source === window) { + return; // event comes from FirefoxCom, no need to replicate + } var event = document.createEvent('CustomEvent'); event.initCustomEvent('find' + e.type, true, true, { query: e.query, diff --git a/web/firefoxcom.js b/web/firefoxcom.js index 8dc01f192..236a0b9ef 100644 --- a/web/firefoxcom.js +++ b/web/firefoxcom.js @@ -148,6 +148,32 @@ Preferences._readFromStorage = function (prefObj) { }); }; +(function listenFindEvents() { + var events = [ + 'find', + 'findagain', + 'findhighlightallchange', + 'findcasesensitivitychange' + ]; + var handleEvent = function (evt) { + if (!PDFViewerApplication.initialized) { + return; + } + PDFViewerApplication.eventBus.dispatch('find', { + source: window, + type: evt.type.substring('find'.length), + query: evt.detail.query, + caseSensitive: !!evt.detail.caseSensitive, + highlightAll: !!evt.detail.highlightAll, + findPrevious: !!evt.detail.findPrevious + }); + }.bind(this); + + for (var i = 0, len = events.length; i < len; i++) { + window.addEventListener(events[i], handleEvent); + } +})(); + function FirefoxComDataRangeTransport(length, initialData) { pdfjsLib.PDFDataRangeTransport.call(this, length, initialData); } diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index b61a3642b..b085a6bc5 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -71,22 +71,6 @@ var PDFFindController = (function PDFFindControllerClosure() { } PDFFindController.prototype = { - listenWindowEvents: function PDFFindController_listenWindowEvents() { - var events = [ - 'find', - 'findagain', - 'findhighlightallchange', - 'findcasesensitivitychange' - ]; - var handleEvent = function (e) { - this.executeCommand(e.type, e.detail); - }.bind(this); - - for (var i = 0, len = events.length; i < len; i++) { - window.addEventListener(events[i], handleEvent); - } - }, - reset: function PDFFindController_reset() { this.startedTextExtraction = false; this.extractTextPromises = [];