diff --git a/web/app.js b/web/app.js index 92629b5eab09c..0b99c7328c63f 100644 --- a/web/app.js +++ b/web/app.js @@ -119,6 +119,8 @@ var PDFViewerApplication = { store: null, /** @type {DownloadManager} */ downloadManager: null, + /** @type {OverlayManager} */ + overlayManager: null, /** @type {Preferences} */ preferences: null, /** @type {Toolbar} */ @@ -261,6 +263,8 @@ var PDFViewerApplication = { let appConfig = this.appConfig; return new Promise((resolve, reject) => { + this.overlayManager = new OverlayManager(); + let eventBus = appConfig.eventBus || getGlobalEventBus(); this.eventBus = eventBus; @@ -329,16 +333,15 @@ var PDFViewerApplication = { this.pdfViewer.setFindController(this.findController); - // FIXME better PDFFindBar constructor parameters + // TODO: improve `PDFFindBar` constructor parameter passing let findBarConfig = Object.create(appConfig.findBar); findBarConfig.findController = this.findController; findBarConfig.eventBus = eventBus; this.findBar = new PDFFindBar(findBarConfig); - this.overlayManager = OverlayManager; - this.pdfDocumentProperties = - new PDFDocumentProperties(appConfig.documentProperties); + new PDFDocumentProperties(appConfig.documentProperties, + this.overlayManager); this.pdfCursorTools = new PDFCursorTools({ container, @@ -361,7 +364,8 @@ var PDFViewerApplication = { }); } - this.passwordPrompt = new PasswordPrompt(appConfig.passwordOverlay); + this.passwordPrompt = new PasswordPrompt(appConfig.passwordOverlay, + this.overlayManager); this.pdfOutlineViewer = new PDFOutlineViewer({ container: appConfig.sidebar.outlineView, @@ -375,7 +379,7 @@ var PDFViewerApplication = { downloadManager, }); - // FIXME better PDFSidebar constructor parameters + // TODO: improve `PDFSidebar` constructor parameter passing let sidebarConfig = Object.create(appConfig.sidebar); sidebarConfig.pdfViewer = this.pdfViewer; sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer; @@ -1911,7 +1915,7 @@ function webViewerClick(evt) { } function webViewerKeyDown(evt) { - if (OverlayManager.active) { + if (PDFViewerApplication.overlayManager.active) { return; } diff --git a/web/chromecom.js b/web/chromecom.js index 70f9411bd6a6e..224f4e9bf5847 100644 --- a/web/chromecom.js +++ b/web/chromecom.js @@ -17,7 +17,6 @@ import { DefaultExternalServices, PDFViewerApplication } from './app'; import { BasePreferences } from './preferences'; import { DownloadManager } from './download_manager'; -import { OverlayManager } from './overlay_manager'; import { PDFJS } from './pdfjs'; if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('CHROME')) { @@ -56,10 +55,11 @@ ChromeCom.request = function ChromeCom_request(action, data, callback) { /** * Resolves a PDF file path and attempts to detects length. * - * @param {String} file Absolute URL of PDF file. - * @param {Function} callback A callback with resolved URL and file length. + * @param {String} file - Absolute URL of PDF file. + * @param {OverlayManager} overlayManager - Manager for the viewer overlays. + * @param {Function} callback - A callback with resolved URL and file length. */ -ChromeCom.resolvePDFFile = function ChromeCom_resolvePDFFile(file, callback) { +ChromeCom.resolvePDFFile = function(file, overlayManager, callback) { // Expand drive:-URLs to filesystem URLs (Chrome OS) file = file.replace(/^drive:/i, 'filesystem:' + location.origin + '/external/'); @@ -110,7 +110,7 @@ ChromeCom.resolvePDFFile = function ChromeCom_resolvePDFFile(file, callback) { if (isAllowedAccess) { callback(file); } else { - requestAccessToLocalFile(file); + requestAccessToLocalFile(file, overlayManager); } }); }); @@ -155,7 +155,7 @@ function reloadIfRuntimeIsUnavailable() { } var chromeFileAccessOverlayPromise; -function requestAccessToLocalFile(fileUrl) { +function requestAccessToLocalFile(fileUrl, overlayManager) { var onCloseOverlay = null; if (top !== window) { // When the extension reloads after receiving new permissions, the pages @@ -169,11 +169,11 @@ function requestAccessToLocalFile(fileUrl) { onCloseOverlay = function() { window.removeEventListener('focus', reloadIfRuntimeIsUnavailable); reloadIfRuntimeIsUnavailable(); - OverlayManager.close('chromeFileAccessOverlay'); + overlayManager.close('chromeFileAccessOverlay'); }; } if (!chromeFileAccessOverlayPromise) { - chromeFileAccessOverlayPromise = OverlayManager.register( + chromeFileAccessOverlayPromise = overlayManager.register( 'chromeFileAccessOverlay', document.getElementById('chromeFileAccessOverlay'), onCloseOverlay, true); @@ -215,7 +215,7 @@ function requestAccessToLocalFile(fileUrl) { // why this permission request is shown. document.getElementById('chrome-url-of-local-file').textContent = fileUrl; - OverlayManager.open('chromeFileAccessOverlay'); + overlayManager.open('chromeFileAccessOverlay'); }); } @@ -338,8 +338,8 @@ class ChromePreferences extends BasePreferences { var ChromeExternalServices = Object.create(DefaultExternalServices); ChromeExternalServices.initPassiveLoading = function (callbacks) { - var appConfig = PDFViewerApplication.appConfig; - ChromeCom.resolvePDFFile(appConfig.defaultUrl, + let { appConfig, overlayManager, } = PDFViewerApplication; + ChromeCom.resolvePDFFile(appConfig.defaultUrl, overlayManager, function (url, length, originalURL) { callbacks.onOpenWithURL(url, length, originalURL); }); diff --git a/web/overlay_manager.js b/web/overlay_manager.js index 7999c417aa662..11a927412c94a 100644 --- a/web/overlay_manager.js +++ b/web/overlay_manager.js @@ -13,132 +13,137 @@ * limitations under the License. */ -var OverlayManager = { - overlays: {}, - active: null, +class OverlayManager { + constructor() { + this._overlays = {}; + this._active = null; + this._keyDownBound = this._keyDown.bind(this); + } + + get active() { + return this._active; + } /** - * @param {string} name The name of the overlay that is registered. - * @param {HTMLDivElement} element The overlay's DOM element. - * @param {function} callerCloseMethod (optional) The method that, if present, - * will call OverlayManager.close from the Object + * @param {string} name - The name of the overlay that is registered. + * @param {HTMLDivElement} element - The overlay's DOM element. + * @param {function} callerCloseMethod - (optional) The method that, if + * present, calls `OverlayManager.close` from the object * registering the overlay. Access to this method is * necessary in order to run cleanup code when e.g. - * the overlay is force closed. The default is null. - * @param {boolean} canForceClose (optional) Indicates if opening the overlay - * will close an active overlay. The default is false. + * the overlay is force closed. The default is `null`. + * @param {boolean} canForceClose - (optional) Indicates if opening the + * overlay closes an active overlay. The default is `false`. * @returns {Promise} A promise that is resolved when the overlay has been * registered. */ - register(name, element, callerCloseMethod, canForceClose) { + register(name, element, callerCloseMethod = null, canForceClose = false) { return new Promise((resolve) => { - var container; + let container; if (!name || !element || !(container = element.parentNode)) { throw new Error('Not enough parameters.'); - } else if (this.overlays[name]) { + } else if (this._overlays[name]) { throw new Error('The overlay is already registered.'); } - this.overlays[name] = { + this._overlays[name] = { element, container, - callerCloseMethod: (callerCloseMethod || null), - canForceClose: (canForceClose || false), + callerCloseMethod, + canForceClose, }; resolve(); }); - }, + } /** - * @param {string} name The name of the overlay that is unregistered. + * @param {string} name - The name of the overlay that is unregistered. * @returns {Promise} A promise that is resolved when the overlay has been * unregistered. */ unregister(name) { return new Promise((resolve) => { - if (!this.overlays[name]) { + if (!this._overlays[name]) { throw new Error('The overlay does not exist.'); - } else if (this.active === name) { + } else if (this._active === name) { throw new Error('The overlay cannot be removed while it is active.'); } - delete this.overlays[name]; - + delete this._overlays[name]; resolve(); }); - }, + } /** - * @param {string} name The name of the overlay that should be opened. + * @param {string} name - The name of the overlay that should be opened. * @returns {Promise} A promise that is resolved when the overlay has been * opened. */ open(name) { return new Promise((resolve) => { - if (!this.overlays[name]) { + if (!this._overlays[name]) { throw new Error('The overlay does not exist.'); - } else if (this.active) { - if (this.overlays[name].canForceClose) { + } else if (this._active) { + if (this._overlays[name].canForceClose) { this._closeThroughCaller(); - } else if (this.active === name) { + } else if (this._active === name) { throw new Error('The overlay is already active.'); } else { throw new Error('Another overlay is currently active.'); } } - this.active = name; - this.overlays[this.active].element.classList.remove('hidden'); - this.overlays[this.active].container.classList.remove('hidden'); + this._active = name; + this._overlays[this._active].element.classList.remove('hidden'); + this._overlays[this._active].container.classList.remove('hidden'); - window.addEventListener('keydown', this._keyDown); + window.addEventListener('keydown', this._keyDownBound); resolve(); }); - }, + } /** - * @param {string} name The name of the overlay that should be closed. + * @param {string} name - The name of the overlay that should be closed. * @returns {Promise} A promise that is resolved when the overlay has been * closed. */ close(name) { return new Promise((resolve) => { - if (!this.overlays[name]) { + if (!this._overlays[name]) { throw new Error('The overlay does not exist.'); - } else if (!this.active) { + } else if (!this._active) { throw new Error('The overlay is currently not active.'); - } else if (this.active !== name) { + } else if (this._active !== name) { throw new Error('Another overlay is currently active.'); } - this.overlays[this.active].container.classList.add('hidden'); - this.overlays[this.active].element.classList.add('hidden'); - this.active = null; + this._overlays[this._active].container.classList.add('hidden'); + this._overlays[this._active].element.classList.add('hidden'); + this._active = null; - window.removeEventListener('keydown', this._keyDown); + window.removeEventListener('keydown', this._keyDownBound); resolve(); }); - }, + } /** * @private */ _keyDown(evt) { - var self = OverlayManager; - if (self.active && evt.keyCode === 27) { // Esc key. - self._closeThroughCaller(); + if (this._active && evt.keyCode === 27) { // Esc key. + this._closeThroughCaller(); evt.preventDefault(); } - }, + } /** * @private */ _closeThroughCaller() { - if (this.overlays[this.active].callerCloseMethod) { - this.overlays[this.active].callerCloseMethod(); + if (this._overlays[this._active].callerCloseMethod) { + this._overlays[this._active].callerCloseMethod(); } - if (this.active) { - this.close(this.active); + if (this._active) { + this.close(this._active); } } -}; +} export { OverlayManager, diff --git a/web/password_prompt.js b/web/password_prompt.js index 8730ba996ae30..895b081b371a7 100644 --- a/web/password_prompt.js +++ b/web/password_prompt.js @@ -14,7 +14,6 @@ */ import { mozL10n } from './ui_utils'; -import { OverlayManager } from './overlay_manager'; import { PasswordResponses } from './pdfjs'; /** @@ -33,14 +32,16 @@ import { PasswordResponses } from './pdfjs'; class PasswordPrompt { /** * @param {PasswordPromptOptions} options + * @param {OverlayManager} overlayManager - Manager for the viewer overlays. */ - constructor(options) { + constructor(options, overlayManager) { this.overlayName = options.overlayName; this.container = options.container; this.label = options.label; this.input = options.input; this.submitButton = options.submitButton; this.cancelButton = options.cancelButton; + this.overlayManager = overlayManager; this.updateCallback = null; this.reason = null; @@ -54,12 +55,12 @@ class PasswordPrompt { } }); - OverlayManager.register(this.overlayName, this.container, - this.close.bind(this), true); + this.overlayManager.register(this.overlayName, this.container, + this.close.bind(this), true); } open() { - OverlayManager.open(this.overlayName).then(() => { + this.overlayManager.open(this.overlayName).then(() => { this.input.focus(); var promptString = mozL10n.get('password_label', null, @@ -75,7 +76,7 @@ class PasswordPrompt { } close() { - OverlayManager.close(this.overlayName).then(() => { + this.overlayManager.close(this.overlayName).then(() => { this.input.value = ''; }); } diff --git a/web/pdf_document_properties.js b/web/pdf_document_properties.js index d84b8df94b354..f0e9edaede0ff 100644 --- a/web/pdf_document_properties.js +++ b/web/pdf_document_properties.js @@ -15,7 +15,6 @@ import { cloneObj, getPDFFileNameFromURL, mozL10n } from './ui_utils'; import { createPromiseCapability } from './pdfjs'; -import { OverlayManager } from './overlay_manager'; const DEFAULT_FIELD_CONTENT = '-'; @@ -30,19 +29,22 @@ const DEFAULT_FIELD_CONTENT = '-'; class PDFDocumentProperties { /** * @param {PDFDocumentPropertiesOptions} options + * @param {OverlayManager} overlayManager - Manager for the viewer overlays. */ - constructor({ overlayName, fields, container, closeButton, }) { + constructor({ overlayName, fields, container, closeButton, }, + overlayManager) { this.overlayName = overlayName; this.fields = fields; this.container = container; + this.overlayManager = overlayManager; this._reset(); if (closeButton) { // Bind the event listener for the Close button. closeButton.addEventListener('click', this.close.bind(this)); } - OverlayManager.register(this.overlayName, this.container, - this.close.bind(this)); + this.overlayManager.register(this.overlayName, this.container, + this.close.bind(this)); } /** @@ -58,7 +60,7 @@ class PDFDocumentProperties { }); }; - Promise.all([OverlayManager.open(this.overlayName), + Promise.all([this.overlayManager.open(this.overlayName), this._dataAvailableCapability.promise]).then(() => { // If the document properties were previously fetched (for this PDF file), // just update the dialog immediately to avoid redundant lookups. @@ -101,7 +103,7 @@ class PDFDocumentProperties { * Close the document properties overlay. */ close() { - OverlayManager.close(this.overlayName); + this.overlayManager.close(this.overlayName); } /** @@ -165,7 +167,7 @@ class PDFDocumentProperties { } return; } - if (OverlayManager.active !== this.overlayName) { + if (this.overlayManager.active !== this.overlayName) { // Don't bother updating the dialog if has already been closed, // since it will be updated the next time `this.open` is called. return; diff --git a/web/pdf_print_service.js b/web/pdf_print_service.js index 5c853e82a73b7..6506b863216a0 100644 --- a/web/pdf_print_service.js +++ b/web/pdf_print_service.js @@ -14,11 +14,11 @@ */ import { CSS_UNITS, mozL10n } from './ui_utils'; -import { OverlayManager } from './overlay_manager'; +import { PDFPrintServiceFactory, PDFViewerApplication } from './app'; import { PDFJS } from './pdfjs'; -import { PDFPrintServiceFactory } from './app'; -var activeService = null; +let activeService = null; +let overlayManager = null; // Renders the page to the canvas of the given print service, and returns // the suggested dimensions of the output page. @@ -118,10 +118,10 @@ PDFPrintService.prototype = { this.scratchCanvas = null; activeService = null; ensureOverlay().then(function () { - if (OverlayManager.active !== 'printServiceOverlay') { + if (overlayManager.active !== 'printServiceOverlay') { return; // overlay was already closed } - OverlayManager.close('printServiceOverlay'); + overlayManager.close('printServiceOverlay'); }); }, @@ -208,7 +208,7 @@ window.print = function print() { } ensureOverlay().then(function () { if (activeService) { - OverlayManager.open('printServiceOverlay'); + overlayManager.open('printServiceOverlay'); } }); @@ -217,9 +217,11 @@ window.print = function print() { } finally { if (!activeService) { console.error('Expected print service to be initialized.'); - if (OverlayManager.active === 'printServiceOverlay') { - OverlayManager.close('printServiceOverlay'); - } + ensureOverlay().then(function () { + if (overlayManager.active === 'printServiceOverlay') { + overlayManager.close('printServiceOverlay'); + } + }); return; // eslint-disable-line no-unsafe-finally } var activeServiceOnEntry = activeService; @@ -310,7 +312,12 @@ if ('onbeforeprint' in window) { var overlayPromise; function ensureOverlay() { if (!overlayPromise) { - overlayPromise = OverlayManager.register('printServiceOverlay', + overlayManager = PDFViewerApplication.overlayManager; + if (!overlayManager) { + throw new Error('The overlay manager has not yet been initialized.'); + } + + overlayPromise = overlayManager.register('printServiceOverlay', document.getElementById('printServiceOverlay'), abort, true); document.getElementById('printCancel').onclick = abort; }