diff --git a/web/app.js b/web/app.js index 2918764df..ed5e6fbe2 100644 --- a/web/app.js +++ b/web/app.js @@ -17,7 +17,7 @@ import { animationStarted, DEFAULT_SCALE_VALUE, getPDFFileNameFromURL, isValidRotation, MAX_SCALE, MIN_SCALE, noContextMenuHandler, normalizeWheelEventDelta, - parseQueryString, ProgressBar, RendererType + parseQueryString, PresentationModeState, ProgressBar, RendererType } from './ui_utils'; import { build, createBlob, getDocument, getFilenameFromUrl, InvalidPDFException, @@ -27,7 +27,6 @@ import { import { CursorTool, PDFCursorTools } from './pdf_cursor_tools'; import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue'; import { PDFSidebar, SidebarView } from './pdf_sidebar'; -import { PDFViewer, PresentationModeState } from './base_viewer'; import { getGlobalEventBus } from './dom_events'; import { OverlayManager } from './overlay_manager'; import { PasswordPrompt } from './password_prompt'; @@ -40,6 +39,7 @@ import { PDFLinkService } from './pdf_link_service'; import { PDFOutlineViewer } from './pdf_outline_viewer'; import { PDFPresentationMode } from './pdf_presentation_mode'; import { PDFThumbnailViewer } from './pdf_thumbnail_viewer'; +import { PDFViewer } from './pdf_viewer'; import { SecondaryToolbar } from './secondary_toolbar'; import { Toolbar } from './toolbar'; import { ViewHistory } from './view_history'; diff --git a/web/base_viewer.js b/web/base_viewer.js index b4f9e3b38..dcb7125de 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -15,9 +15,9 @@ import { createPromiseCapability, PDFJS } from 'pdfjs-lib'; import { - CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, getVisibleElements, - isValidRotation, MAX_AUTO_SCALE, NullL10n, RendererType, SCROLLBAR_PADDING, - scrollIntoView, UNKNOWN_SCALE, VERTICAL_PADDING, watchScroll + CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, isValidRotation, + MAX_AUTO_SCALE, NullL10n, PresentationModeState, RendererType, + SCROLLBAR_PADDING, UNKNOWN_SCALE, VERTICAL_PADDING, watchScroll } from './ui_utils'; import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue'; import { AnnotationLayerBuilder } from './annotation_layer_builder'; @@ -26,13 +26,6 @@ import { PDFPageView } from './pdf_page_view'; import { SimpleLinkService } from './pdf_link_service'; import { TextLayerBuilder } from './text_layer_builder'; -const PresentationModeState = { - UNKNOWN: 0, - NORMAL: 1, - CHANGING: 2, - FULLSCREEN: 3, -}; - const DEFAULT_CACHE_SIZE = 10; /** @@ -60,7 +53,7 @@ const DEFAULT_CACHE_SIZE = 10; function PDFPageViewBuffer(size) { let data = []; - this.push = function cachePush(view) { + this.push = function(view) { let i = data.indexOf(view); if (i >= 0) { data.splice(i, 1); @@ -70,7 +63,7 @@ function PDFPageViewBuffer(size) { data.shift().destroy(); } }; - this.resize = function (newSize) { + this.resize = function(newSize) { size = newSize; while (data.length > size) { data.shift().destroy(); @@ -98,11 +91,16 @@ function isPortraitOrientation(size) { * Simple viewer control to display PDF content/pages. * @implements {IRenderableView} */ -class PDFViewer { +class BaseViewer { /** * @param {PDFViewerOptions} options */ constructor(options) { + if (this.constructor === BaseViewer) { + throw new Error('Cannot initialize BaseViewer.'); + } + this._name = this.constructor.name; + this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; this.eventBus = options.eventBus || getGlobalEventBus(); @@ -182,7 +180,7 @@ class PDFViewer { if (!(0 < val && val <= this.pagesCount)) { console.error( - `PDFViewer._setCurrentPageNumber: "${val}" is out of bounds.`); + `${this._name}._setCurrentPageNumber: "${val}" is out of bounds.`); return; } @@ -305,6 +303,10 @@ class PDFViewer { } } + get _setDocumentViewerElement() { + throw new Error('Not implemented: _setDocumentViewerElement'); + } + /** * @param pdfDocument {PDFDocument} */ @@ -364,7 +366,7 @@ class PDFViewer { textLayerFactory = this; } let pageView = new PDFPageView({ - container: this.viewer, + container: this._setDocumentViewerElement, eventBus: this.eventBus, id: pageNum, scale, @@ -437,7 +439,7 @@ class PDFViewer { } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) { this._pageLabels = null; - console.error('PDFViewer.setPageLabels: Invalid page labels.'); + console.error(`${this._name}.setPageLabels: Invalid page labels.`); } else { this._pageLabels = labels; } @@ -472,6 +474,10 @@ class PDFViewer { this.update(); } + _scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null, }) { + throw new Error('Not implemented: _scrollIntoView'); + } + _setScaleDispatchEvent(newScale, newValue, preset = false) { let arg = { source: this, @@ -560,7 +566,7 @@ class PDFViewer { break; default: console.error( - `PDFViewer._setScale: "${value}" is an unknown zoom value.`); + `${this._name}._setScale: "${value}" is an unknown zoom value.`); return; } this._setScaleUpdatePages(scale, value, noScroll, /* preset = */ true); @@ -578,7 +584,7 @@ class PDFViewer { } let pageView = this._pages[this._currentPageNumber - 1]; - scrollIntoView(pageView.div); + this._scrollIntoView({ pageDiv: pageView.div, }); } /** @@ -615,7 +621,7 @@ class PDFViewer { let pageView = this._pages[pageNumber - 1]; if (!pageView) { console.error( - 'PDFViewer.scrollPageIntoView: Invalid "pageNumber" parameter.'); + `${this._name}.scrollPageIntoView: Invalid "pageNumber" parameter.`); return; } let x = 0, y = 0; @@ -675,7 +681,7 @@ class PDFViewer { scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); break; default: - console.error(`PDFViewer.scrollPageIntoView: "${dest[1].name}" ` + + console.error(`${this._name}.scrollPageIntoView: "${dest[1].name}" ` + 'is not a valid destination type.'); return; } @@ -687,7 +693,10 @@ class PDFViewer { } if (scale === 'page-fit' && !dest[4]) { - scrollIntoView(pageView.div); + this._scrollIntoView({ + pageDiv: pageView.div, + pageNumber, + }); return; } @@ -705,7 +714,17 @@ class PDFViewer { left = Math.max(left, 0); top = Math.max(top, 0); } - scrollIntoView(pageView.div, { left, top, }); + this._scrollIntoView({ + pageDiv: pageView.div, + pageSpot: { left, top, }, + pageNumber, + }); + } + + _resizeBuffer(numVisiblePages) { + let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, + 2 * numVisiblePages + 1); + this._buffer.resize(suggestedCacheSize); } _updateLocation(firstPage) { @@ -738,48 +757,7 @@ class PDFViewer { } update() { - let visible = this._getVisiblePages(); - let visiblePages = visible.views; - if (visiblePages.length === 0) { - return; - } - - let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, - 2 * visiblePages.length + 1); - this._buffer.resize(suggestedCacheSize); - - this.renderingQueue.renderHighestPriority(visible); - - let currentId = this._currentPageNumber; - let firstPage = visible.first; - let stillFullyVisible = false; - - for (let i = 0, ii = visiblePages.length; i < ii; ++i) { - let page = visiblePages[i]; - - if (page.percent < 100) { - break; - } - if (page.id === currentId) { - stillFullyVisible = true; - break; - } - } - - if (!stillFullyVisible) { - currentId = visiblePages[0].id; - } - - if (!this.isInPresentationMode) { - this._setCurrentPageNumber(currentId); - } - - this._updateLocation(firstPage); - - this.eventBus.dispatch('updateviewarea', { - source: this, - location: this._location, - }); + throw new Error('Not implemented: update'); } containsElement(element) { @@ -804,15 +782,7 @@ class PDFViewer { } _getVisiblePages() { - if (!this.isInPresentationMode) { - return getVisibleElements(this.container, this._pages, true); - } - // The algorithm in getVisibleElements doesn't work in all browsers and - // configurations when presentation mode is active. - let visible = []; - let currentPage = this._pages[this._currentPageNumber - 1]; - visible.push({ id: currentPage.id, view: currentPage, }); - return { first: currentPage, last: currentPage, views: visible, }; + throw new Error('Not implemented: _getVisiblePages'); } cleanup() { @@ -974,6 +944,5 @@ class PDFViewer { } export { - PresentationModeState, - PDFViewer, + BaseViewer, }; diff --git a/web/pdf_viewer.component.js b/web/pdf_viewer.component.js index 42b7cba2a..584aa9ed6 100644 --- a/web/pdf_viewer.component.js +++ b/web/pdf_viewer.component.js @@ -16,7 +16,7 @@ 'use strict'; var pdfjsLib = require('./pdfjs.js'); -var pdfjsWebPDFViewer = require('./base_viewer.js'); +var pdfjsWebPDFViewer = require('./pdf_viewer.js'); var pdfjsWebPDFPageView = require('./pdf_page_view.js'); var pdfjsWebPDFLinkService = require('./pdf_link_service.js'); var pdfjsWebTextLayerBuilder = require('./text_layer_builder.js'); diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js new file mode 100644 index 000000000..c32065ff6 --- /dev/null +++ b/web/pdf_viewer.js @@ -0,0 +1,83 @@ +/* Copyright 2014 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. + */ + +import { getVisibleElements, scrollIntoView } from './ui_utils'; +import { BaseViewer } from './base_viewer'; +import { shadow } from 'pdfjs-lib'; + +class PDFViewer extends BaseViewer { + get _setDocumentViewerElement() { + return shadow(this, '_setDocumentViewerElement', this.viewer); + } + + _scrollIntoView({ pageDiv, pageSpot = null, }) { + scrollIntoView(pageDiv, pageSpot); + } + + _getVisiblePages() { + if (!this.isInPresentationMode) { + return getVisibleElements(this.container, this._pages, true); + } + // The algorithm in getVisibleElements doesn't work in all browsers and + // configurations when presentation mode is active. + let currentPage = this._pages[this._currentPageNumber - 1]; + let visible = [{ id: currentPage.id, view: currentPage, }]; + return { first: currentPage, last: currentPage, views: visible, }; + } + + update() { + let visible = this._getVisiblePages(); + let visiblePages = visible.views, numVisiblePages = visiblePages.length; + + if (numVisiblePages === 0) { + return; + } + this._resizeBuffer(numVisiblePages); + + this.renderingQueue.renderHighestPriority(visible); + + let currentId = this._currentPageNumber; + let stillFullyVisible = false; + + for (let i = 0; i < numVisiblePages; ++i) { + let page = visiblePages[i]; + + if (page.percent < 100) { + break; + } + if (page.id === currentId) { + stillFullyVisible = true; + break; + } + } + + if (!stillFullyVisible) { + currentId = visiblePages[0].id; + } + if (!this.isInPresentationMode) { + this._setCurrentPageNumber(currentId); + } + + this._updateLocation(visible.first); + this.eventBus.dispatch('updateviewarea', { + source: this, + location: this._location, + }); + } +} + +export { + PDFViewer, +}; diff --git a/web/ui_utils.js b/web/ui_utils.js index aec8a5793..394e1e625 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -25,6 +25,13 @@ const MAX_AUTO_SCALE = 1.25; const SCROLLBAR_PADDING = 40; const VERTICAL_PADDING = 5; +const PresentationModeState = { + UNKNOWN: 0, + NORMAL: 1, + CHANGING: 2, + FULLSCREEN: 3, +}; + const RendererType = { CANVAS: 'canvas', SVG: 'svg', @@ -661,6 +668,7 @@ export { VERTICAL_PADDING, isValidRotation, cloneObj, + PresentationModeState, RendererType, mozL10n, NullL10n,