From 7af8748151b174fc2206a6ed543b5474a589fb25 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Fri, 12 Sep 2014 21:27:45 -0500 Subject: [PATCH] Moves viewer code into PDFViewer and some code from PageView. --- web/page_view.js | 88 +++----- web/pdf_find_controller.js | 10 +- web/pdf_viewer.js | 314 +++++++++++++++++++++++++++ web/presentation_mode.js | 6 +- web/viewer.html | 1 + web/viewer.js | 428 +++++++++++++------------------------ 6 files changed, 502 insertions(+), 345 deletions(-) create mode 100644 web/pdf_viewer.js diff --git a/web/page_view.js b/web/page_view.js index 62a90686d..e5663272f 100644 --- a/web/page_view.js +++ b/web/page_view.js @@ -14,15 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals RenderingStates, PDFView, PDFHistory, PDFJS, mozL10n, CustomStyle, +/* globals RenderingStates, PDFView, PDFJS, mozL10n, CustomStyle, PresentationMode, scrollIntoView, SCROLLBAR_PADDING, CSS_UNITS, UNKNOWN_SCALE, DEFAULT_SCALE, getOutputScale, TextLayerBuilder, - cache, Stats */ + Stats */ 'use strict'; -var PageView = function pageView(container, id, scale, - navigateTo, defaultViewport) { +var PageView = function pageView(container, id, scale, defaultViewport, + linkService, renderingQueue, cache, viewer) { this.id = id; this.rotation = 0; @@ -31,6 +31,11 @@ var PageView = function pageView(container, id, scale, this.pdfPageRotate = defaultViewport.rotation; this.hasRestrictedScaling = false; + this.linkService = linkService; + this.renderingQueue = renderingQueue; + this.cache = cache; + this.viewer = viewer; + this.renderingState = RenderingStates.INITIAL; this.resume = null; @@ -241,10 +246,10 @@ var PageView = function pageView(container, id, scale, function setupAnnotations(pageDiv, pdfPage, viewport) { function bindLink(link, dest) { - link.href = PDFView.getDestinationHash(dest); + link.href = linkService.getDestinationHash(dest); link.onclick = function pageViewSetupLinksOnclick() { if (dest) { - PDFView.navigateTo(dest); + linkService.navigateTo(dest); } return false; }; @@ -254,47 +259,9 @@ var PageView = function pageView(container, id, scale, } function bindNamedAction(link, action) { - link.href = PDFView.getAnchorUrl(''); + link.href = linkService.getAnchorUrl(''); link.onclick = function pageViewSetupNamedActionOnClick() { - // See PDF reference, table 8.45 - Named action - switch (action) { - case 'GoToPage': - document.getElementById('pageNumber').focus(); - break; - - case 'GoBack': - PDFHistory.back(); - break; - - case 'GoForward': - PDFHistory.forward(); - break; - - case 'Find': - if (!PDFView.supportsIntegratedFind) { - PDFView.findBar.toggle(); - } - break; - - case 'NextPage': - PDFView.page++; - break; - - case 'PrevPage': - PDFView.page--; - break; - - case 'LastPage': - PDFView.page = PDFView.pages.length; - break; - - case 'FirstPage': - PDFView.page = 1; - break; - - default: - break; // No action according to spec - } + linkService.executeNamedAction(action); return false; }; link.className = 'internalLink'; @@ -377,13 +344,13 @@ var PageView = function pageView(container, id, scale, this.scrollIntoView = function pageViewScrollIntoView(dest) { if (PresentationMode.active) { - if (PDFView.page !== this.id) { - // Avoid breaking PDFView.getVisiblePages in presentation mode. - PDFView.page = this.id; + if (this.linkService.page !== this.id) { + // Avoid breaking getVisiblePages in presentation mode. + this.linkService.page = this.id; return; } dest = null; - PDFView.setScale(PDFView.currentScaleValue, true, true); + this.viewer.setScale(this.viewer.currentScaleValue, true, true); } if (!dest) { scrollIntoView(div); @@ -431,9 +398,10 @@ var PageView = function pageView(container, id, scale, y = dest[3]; width = dest[4] - x; height = dest[5] - y; - widthScale = (PDFView.container.clientWidth - SCROLLBAR_PADDING) / + var viewerContainer = this.viewer.container; + widthScale = (viewerContainer.clientWidth - SCROLLBAR_PADDING) / width / CSS_UNITS; - heightScale = (PDFView.container.clientHeight - SCROLLBAR_PADDING) / + heightScale = (viewerContainer.clientHeight - SCROLLBAR_PADDING) / height / CSS_UNITS; scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); break; @@ -441,10 +409,10 @@ var PageView = function pageView(container, id, scale, return; } - if (scale && scale !== PDFView.currentScale) { - PDFView.setScale(scale, true, true); - } else if (PDFView.currentScale === UNKNOWN_SCALE) { - PDFView.setScale(DEFAULT_SCALE, true, true); + if (scale && scale !== this.viewer.currentScale) { + this.viewer.setScale(scale, true, true); + } else if (this.viewer.currentScale === UNKNOWN_SCALE) { + this.viewer.setScale(DEFAULT_SCALE, true, true); } if (scale === 'page-fit' && !dest[4]) { @@ -463,7 +431,7 @@ var PageView = function pageView(container, id, scale, }; this.getTextContent = function pageviewGetTextContent() { - return PDFView.getPage(this.id).then(function(pdfPage) { + return this.renderingQueue.getPage(this.id).then(function(pdfPage) { return pdfPage.getTextContent(); }); }; @@ -475,7 +443,7 @@ var PageView = function pageView(container, id, scale, return; } if (!pdfPage) { - var promise = PDFView.getPage(this.id); + var promise = this.renderingQueue.getPage(this.id); promise.then(function(pdfPage) { delete this.pagePdfPromise; this.setPdfPage(pdfPage); @@ -559,7 +527,7 @@ var PageView = function pageView(container, id, scale, textLayerDiv ? new TextLayerBuilder({ textLayerDiv: textLayerDiv, pageIndex: this.id - 1, - lastScrollSource: PDFView, + lastScrollSource: this.linkService, viewport: this.viewport, isViewerInPresentationMode: PresentationMode.active, findController: PDFView.findController @@ -646,7 +614,7 @@ var PageView = function pageView(container, id, scale, viewport: this.viewport, // intent: 'default', // === 'display' continueCallback: function pdfViewcContinueCallback(cont) { - if (PDFView.highestPriorityPage !== 'page' + self.id) { + if (self.renderingQueue.highestPriorityPage !== 'page' + self.id) { self.renderingState = RenderingStates.PAUSED; self.resume = function resumeCallback() { self.renderingState = RenderingStates.RUNNING; diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 6459faf27..46c724d0e 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -137,7 +137,7 @@ var PDFFindController = (function PDFFindControllerClosure() { this.pageContents = []; var extractTextPromisesResolves = []; - var numPages = this.pdfPageSource.pdfDocument.numPages; + var numPages = this.pdfPageSource.pagesCount; for (var i = 0; i < numPages; i++) { this.extractTextPromises.push(new Promise(function (resolve) { extractTextPromisesResolves.push(resolve); @@ -146,7 +146,7 @@ var PDFFindController = (function PDFFindControllerClosure() { var self = this; function extractPageText(pageIndex) { - self.pdfPageSource.pages[pageIndex].getTextContent().then( + self.pdfPageSource.getPageView(pageIndex).getTextContent().then( function textContentResolved(textContent) { var textItems = textContent.items; var str = []; @@ -159,7 +159,7 @@ var PDFFindController = (function PDFFindControllerClosure() { self.pageContents.push(str.join('')); extractTextPromisesResolves[pageIndex](pageIndex); - if ((pageIndex + 1) < self.pdfPageSource.pages.length) { + if ((pageIndex + 1) < self.pdfPageSource.pagesCount) { extractPageText(pageIndex + 1); } } @@ -189,7 +189,7 @@ var PDFFindController = (function PDFFindControllerClosure() { }, updatePage: function PDFFindController_updatePage(index) { - var page = this.pdfPageSource.pages[index]; + var page = this.pdfPageSource.getPageView(index); if (this.selected.pageIdx === index) { // If the page is selected, scroll the page into view, which triggers @@ -206,7 +206,7 @@ var PDFFindController = (function PDFFindControllerClosure() { nextMatch: function PDFFindController_nextMatch() { var previous = this.state.findPrevious; var currentPageIndex = this.pdfPageSource.page - 1; - var numPages = this.pdfPageSource.pages.length; + var numPages = this.pdfPageSource.pagesCount; this.active = true; diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js new file mode 100644 index 000000000..f2bc25758 --- /dev/null +++ b/web/pdf_viewer.js @@ -0,0 +1,314 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* 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. + */ + /*globals watchScroll, Cache, DEFAULT_CACHE_SIZE, PageView, UNKNOWN_SCALE, + IGNORE_CURRENT_POSITION_ON_ZOOM, SCROLLBAR_PADDING, VERTICAL_PADDING, + MAX_AUTO_SCALE, getVisibleElements, PresentationMode, + RenderingStates */ + +'use strict'; + +var PDFViewer = (function pdfViewer() { + function PDFViewer(options) { + this.container = options.container; + this.viewer = options.viewer; + this.renderingQueue = options.renderingQueue; + this.linkService = options.linkService; + + this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this)); + this.pages = []; + this.cache = new Cache(DEFAULT_CACHE_SIZE); + this.currentPageNumber = 1; + this.previousPageNumber = 1; + this.updateInProgress = true; + this.resetView(); + } + + PDFViewer.prototype = { + get pagesCount() { + return this.pages.length; + }, + + setCurrentPageNumber: function (val) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', true, true, window, 0); + event.updateInProgress = this.updateInProgress; + + if (!(0 < val && val <= this.pagesCount)) { + this.previousPageNumber = val; + event.pageNumber = this.page; + this.container.dispatchEvent(event); + return; + } + + this.pages[val - 1].updateStats(); + this.previousPageNumber = this.currentPageNumber; + this.currentPageNumber = val; + event.pageNumber = val; + this.container.dispatchEvent(event); + }, + + addPage: function (pageNum, scale, viewport) { + var pageView = new PageView(this.viewer, pageNum, scale, viewport, + this.linkService, this.renderingQueue, + this.cache, this); + this.pages.push(pageView); + return pageView; + }, + + resetView: function () { + this.currentScale = UNKNOWN_SCALE; + this.currentScaleValue = null; + this.location = null; + }, + + _scrollUpdate: function () { + if (this.pagesCount === 0) { + return; + } + this.update(); + }, + + _setScaleUpdatePages: function pdfViewer_setScaleUpdatePages( + newScale, newValue, resetAutoSettings, noScroll, preset) { + this.currentScaleValue = newValue; + if (newScale === this.currentScale) { + return; + } + for (var i = 0, ii = this.pages.length; i < ii; i++) { + this.pages[i].update(newScale); + } + this.currentScale = newScale; + + if (!noScroll) { + var page = this.currentPageNumber, dest; + if (this.location && !this.inPresentationMode && + !IGNORE_CURRENT_POSITION_ON_ZOOM) { + page = this.location.pageNumber; + dest = [null, { name: 'XYZ' }, this.location.left, + this.location.top, null]; + } + this.pages[page - 1].scrollIntoView(dest); + } + + var event = document.createEvent('UIEvents'); + event.initUIEvent('scalechange', true, true, window, 0); + event.scale = newScale; + event.resetAutoSettings = resetAutoSettings; + if (preset) { + event.presetValue = newValue; + } + this.container.dispatchEvent(event); + }, + + setScale: function pdfViewer_setScale(value, resetAutoSettings, noScroll) { + if (value === 'custom') { + return; + } + var scale = parseFloat(value); + + if (scale > 0) { + this._setScaleUpdatePages(scale, value, true, noScroll, false); + } else { + var currentPage = this.pages[this.currentPageNumber - 1]; + if (!currentPage) { + return; + } + var hPadding = PresentationMode.active ? 0 : SCROLLBAR_PADDING; + var vPadding = PresentationMode.active ? 0 : VERTICAL_PADDING; + var pageWidthScale = (this.container.clientWidth - hPadding) / + currentPage.width * currentPage.scale; + var pageHeightScale = (this.container.clientHeight - vPadding) / + currentPage.height * currentPage.scale; + switch (value) { + case 'page-actual': + scale = 1; + break; + case 'page-width': + scale = pageWidthScale; + break; + case 'page-height': + scale = pageHeightScale; + break; + case 'page-fit': + scale = Math.min(pageWidthScale, pageHeightScale); + break; + case 'auto': + var isLandscape = (currentPage.width > currentPage.height); + var horizontalScale = isLandscape ? pageHeightScale : + pageWidthScale; + scale = Math.min(MAX_AUTO_SCALE, horizontalScale); + break; + default: + console.error('pdfViewSetScale: \'' + value + + '\' is an unknown zoom value.'); + return; + } + this._setScaleUpdatePages(scale, value, resetAutoSettings, noScroll, + true); + } + }, + + updateRotation: function pdfViewRotatePages(rotation) { + for (var i = 0, l = this.pages.length; i < l; i++) { + var page = this.pages[i]; + page.update(page.scale, rotation); + } + + this.setScale(this.currentScaleValue, true, true); + }, + + removeAllPages: function () { + var container = this.viewer; + while (container.hasChildNodes()) { + container.removeChild(container.lastChild); + } + this.pages = []; + }, + + updateLocation: function (firstPage) { + var currentScale = this.currentScale; + var currentScaleValue = this.currentScaleValue; + var normalizedScaleValue = + parseFloat(currentScaleValue) === currentScale ? + Math.round(currentScale * 10000) / 100 : currentScaleValue; + + var pageNumber = firstPage.id; + var pdfOpenParams = '#page=' + pageNumber; + pdfOpenParams += '&zoom=' + normalizedScaleValue; + var currentPageView = this.pages[pageNumber - 1]; + var container = this.container; + var topLeft = currentPageView.getPagePoint( + (container.scrollLeft - firstPage.x), + (container.scrollTop - firstPage.y)); + var intLeft = Math.round(topLeft[0]); + var intTop = Math.round(topLeft[1]); + pdfOpenParams += ',' + intLeft + ',' + intTop; + + this.location = { + pageNumber: pageNumber, + scale: normalizedScaleValue, + top: intTop, + left: intLeft, + pdfOpenParams: pdfOpenParams + }; + }, + + get inPresentationMode() { + return PresentationMode.active || PresentationMode.switchInProgress; + }, + + update: function () { + var visible = this.getVisiblePages(); + var visiblePages = visible.views; + if (visiblePages.length === 0) { + return; + } + + this.updateInProgress = true; + + var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, + 2 * visiblePages.length + 1); + this.cache.resize(suggestedCacheSize); + + this.renderingQueue.renderHighestPriority(visible); + + var currentId = this.currentPageNumber; + var firstPage = visible.first; + + for (var i = 0, ii = visiblePages.length, stillFullyVisible = false; + i < ii; ++i) { + var page = visiblePages[i]; + + if (page.percent < 100) { + break; + } + if (page.id === this.currentPageNumber) { + stillFullyVisible = true; + break; + } + } + + if (!stillFullyVisible) { + currentId = visiblePages[0].id; + } + + if (!PresentationMode.active) { + this.setCurrentPageNumber(currentId); + } + + this.updateLocation(firstPage); + + this.updateInProgress = false; + + var event = document.createEvent('UIEvents'); + event.initUIEvent('updateviewarea', true, true, window, 0); + this.container.dispatchEvent(event); + }, + + containsElement: function (element) { + return this.container.contains(element); + }, + + focus: function () { + this.container.focus(); + }, + + blur: function () { + this.container.blur(); + }, + + get isHorizontalScrollbarEnabled() { + return (PresentationMode.active ? false : + (this.container.scrollWidth > this.container.clientWidth)); + }, + + getVisiblePages: function () { + if (!PresentationMode.active) { + return getVisibleElements(this.container, this.pages, true); + } else { + // The algorithm in getVisibleElements doesn't work in all browsers and + // configurations when presentation mode is active. + var visible = []; + var currentPage = this.pages[this.currentPageNumber - 1]; + visible.push({ id: currentPage.id, view: currentPage }); + return { first: currentPage, last: currentPage, views: visible }; + } + }, + + cleanup: function () { + for (var i = 0, ii = this.pages.length; i < ii; i++) { + if (this.pages[i] && + this.pages[i].renderingState !== RenderingStates.FINISHED) { + this.pages[i].reset(); + } + } + }, + + forceRendering: function (currentlyVisiblePages) { + var visiblePages = currentlyVisiblePages || this.getVisiblePages(); + var pageView = this.renderingQueue.getHighestPriority(visiblePages, + this.pages, + this.scroll.down); + if (pageView) { + this.renderingQueue.renderView(pageView, 'page'); + return; + } + }, + }; + + return PDFViewer; +})(); diff --git a/web/presentation_mode.js b/web/presentation_mode.js index 46a4fc2de..f297623c8 100644 --- a/web/presentation_mode.js +++ b/web/presentation_mode.js @@ -68,7 +68,7 @@ var PresentationMode = { }, /** - * Initialize a timeout that is used to reset PDFView.currentPosition when the + * Initialize a timeout that is used to specify switchInProgress when the * browser transitions to fullscreen mode. Since resize events are triggered * multiple times during the switch to fullscreen mode, this is necessary in * order to prevent the page from being scrolled partially, or completely, @@ -82,8 +82,6 @@ var PresentationMode = { this.switchInProgress = setTimeout(function switchInProgressTimeout() { delete this.switchInProgress; }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); - - PDFView.currentPosition = null; }, _resetSwitchInProgress: function presentationMode_resetSwitchInProgress() { @@ -150,7 +148,7 @@ var PresentationMode = { // Note: This is only necessary in non-Mozilla browsers. setTimeout(function exitPresentationModeTimeout() { this.active = false; - PDFView.setScale(this.args.previousScale); + PDFView.setScale(this.args.previousScale, true); PDFView.page = page; this.args = null; }.bind(this), 0); diff --git a/web/viewer.html b/web/viewer.html index d15c3456d..6df149d18 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -69,6 +69,7 @@ http://sourceforge.net/adobe/cmap/wiki/License/ + diff --git a/web/viewer.js b/web/viewer.js index ad9d90625..48ff868ab 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -21,7 +21,7 @@ PasswordPrompt, PresentationMode, HandTool, Promise, DocumentProperties, DocumentOutlineView, DocumentAttachmentsView, OverlayManager, PDFFindController, PDFFindBar, getVisibleElements, - watchScroll */ + watchScroll, PDFViewer */ 'use strict'; @@ -92,9 +92,6 @@ var mozL10n = document.mozL10n || document.webL10n; //#include chromecom.js //#endif -var cache = new Cache(DEFAULT_CACHE_SIZE); -var currentPageNumber = 1; - //#include view_history.js //#include pdf_find_bar.js //#include pdf_find_controller.js @@ -107,33 +104,32 @@ var currentPageNumber = 1; //#include document_properties.js var PDFView = { - pages: [], - currentScale: UNKNOWN_SCALE, - currentScaleValue: null, initialBookmark: document.location.hash.substring(1), - container: null, initialized: false, fellback: false, pdfDocument: null, sidebarOpen: false, printing: false, - pageViewScroll: null, + pdfViewer: null, pdfThumbnailViewer: null, pageRotation: 0, mouseScrollTimeStamp: 0, mouseScrollDelta: 0, lastScroll: 0, - previousPageNumber: 1, isViewerEmbedded: (window.parent !== window), idleTimeout: null, - currentPosition: null, url: '', // called once when the document is loaded initialize: function pdfViewInitialize() { - var self = this; - var container = this.container = document.getElementById('viewerContainer'); - this.pageViewScroll = watchScroll(container, updateViewarea); + var container = document.getElementById('viewerContainer'); + var viewer = document.getElementById('viewer'); + this.pdfViewer = new PDFViewer({ + container: container, + viewer: viewer, + renderingQueue: this, + linkService: this + }); var thumbnailContainer = document.getElementById('thumbnailView'); this.pdfThumbnailViewer = new PDFThumbnailViewer({ @@ -271,133 +267,44 @@ var PDFView = { return this.pdfDocument.getPage(n); }, - _setScaleUpdatePages: function pdfView_setScaleUpdatePages( - newScale, newValue, resetAutoSettings, noScroll) { - this.currentScaleValue = newValue; - if (newScale === this.currentScale) { - return; - } - for (var i = 0, ii = this.pages.length; i < ii; i++) { - this.pages[i].update(newScale); - } - this.currentScale = newScale; - - if (!noScroll) { - var page = this.page, dest; - if (this.currentPosition && !IGNORE_CURRENT_POSITION_ON_ZOOM) { - page = this.currentPosition.page; - dest = [null, { name: 'XYZ' }, this.currentPosition.left, - this.currentPosition.top, null]; - } - this.pages[page - 1].scrollIntoView(dest); - } - var event = document.createEvent('UIEvents'); - event.initUIEvent('scalechange', false, false, window, 0); - event.scale = newScale; - event.resetAutoSettings = resetAutoSettings; - window.dispatchEvent(event); - }, - - setScale: function pdfViewSetScale(value, resetAutoSettings, noScroll) { - if (value === 'custom') { - return; - } - var scale = parseFloat(value); - - if (scale > 0) { - this._setScaleUpdatePages(scale, value, true, noScroll); - } else { - var currentPage = this.pages[this.page - 1]; - if (!currentPage) { - return; - } - var hPadding = PresentationMode.active ? 0 : SCROLLBAR_PADDING; - var vPadding = PresentationMode.active ? 0 : VERTICAL_PADDING; - var pageWidthScale = (this.container.clientWidth - hPadding) / - currentPage.width * currentPage.scale; - var pageHeightScale = (this.container.clientHeight - vPadding) / - currentPage.height * currentPage.scale; - switch (value) { - case 'page-actual': - scale = 1; - break; - case 'page-width': - scale = pageWidthScale; - break; - case 'page-height': - scale = pageHeightScale; - break; - case 'page-fit': - scale = Math.min(pageWidthScale, pageHeightScale); - break; - case 'auto': - var isLandscape = (currentPage.width > currentPage.height); - var horizontalScale = isLandscape ? pageHeightScale : pageWidthScale; - scale = Math.min(MAX_AUTO_SCALE, horizontalScale); - break; - default: - console.error('pdfViewSetScale: \'' + value + - '\' is an unknown zoom value.'); - return; - } - this._setScaleUpdatePages(scale, value, resetAutoSettings, noScroll); - - selectScaleOption(value); - } + getPageView: function pdfViewGetPageView(index) { + return this.pdfViewer.pages[index]; }, zoomIn: function pdfViewZoomIn(ticks) { - var newScale = this.currentScale; + var newScale = this.pdfViewer.currentScale; do { newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2); newScale = Math.ceil(newScale * 10) / 10; newScale = Math.min(MAX_SCALE, newScale); } while (--ticks && newScale < MAX_SCALE); - this.setScale(newScale, true); + this.pdfViewer.setScale(newScale, true); }, zoomOut: function pdfViewZoomOut(ticks) { - var newScale = this.currentScale; + var newScale = this.pdfViewer.currentScale; do { newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2); newScale = Math.floor(newScale * 10) / 10; newScale = Math.max(MIN_SCALE, newScale); } while (--ticks && newScale > MIN_SCALE); - this.setScale(newScale, true); + this.pdfViewer.setScale(newScale, true); + }, + + get currentScaleValue() { + return this.pdfViewer.currentScaleValue; + }, + + get pagesCount() { + return this.pdfDocument.numPages; }, set page(val) { - var pages = this.pages; - var event = document.createEvent('UIEvents'); - event.initUIEvent('pagechange', false, false, window, 0); - - if (!(0 < val && val <= pages.length)) { - this.previousPageNumber = val; - event.pageNumber = this.page; - window.dispatchEvent(event); - return; - } - - pages[val - 1].updateStats(); - this.previousPageNumber = currentPageNumber; - currentPageNumber = val; - event.pageNumber = val; - window.dispatchEvent(event); - - // checking if the this.page was called from the updateViewarea function: - // avoiding the creation of two "set page" method (internal and public) - if (updateViewarea.inProgress) { - return; - } - // Avoid scrolling the first page during loading - if (this.loading && val === 1) { - return; - } - pages[val - 1].scrollIntoView(); + this.pdfViewer.setCurrentPageNumber(val); }, get page() { - return currentPageNumber; + return this.pdfViewer.currentPageNumber; }, get supportsPrinting() { @@ -478,11 +385,6 @@ var PDFView = { return bar; }, - get isHorizontalScrollbarEnabled() { - return (PresentationMode.active ? false : - (this.container.scrollWidth > this.container.clientWidth)); - }, - //#if (FIREFOX || MOZCENTRAL) initPassiveLoading: function pdfViewInitPassiveLoading() { var pdfDataRangeTransport = { @@ -590,11 +492,7 @@ var PDFView = { this.pdfDocument = null; this.pdfThumbnailViewer.removeAllThumbnails(); - - var container = document.getElementById('viewer'); - while (container.hasChildNodes()) { - container.removeChild(container.lastChild); - } + this.pdfViewer.removeAllPages(); if (typeof PDFBug !== 'undefined') { PDFBug.cleanup(); @@ -745,10 +643,10 @@ var PDFView = { self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1); if (pageNumber) { - if (pageNumber > self.pages.length) { - pageNumber = self.pages.length; + if (pageNumber > self.pagesCount) { + pageNumber = self.pagesCount; } - var currentPage = self.pages[pageNumber - 1]; + var currentPage = self.getPageView(pageNumber - 1); currentPage.scrollIntoView(dest); // Update the browsing history. @@ -774,6 +672,48 @@ var PDFView = { }); }, + executeNamedAction: function pdfViewExecuteNamedAction(action) { + // See PDF reference, table 8.45 - Named action + switch (action) { + case 'GoToPage': + document.getElementById('pageNumber').focus(); + break; + + case 'GoBack': + PDFHistory.back(); + break; + + case 'GoForward': + PDFHistory.forward(); + break; + + case 'Find': + if (!PDFView.supportsIntegratedFind) { + PDFView.findBar.toggle(); + } + break; + + case 'NextPage': + PDFView.page++; + break; + + case 'PrevPage': + PDFView.page--; + break; + + case 'LastPage': + PDFView.page = PDFView.pagesCount; + break; + + case 'FirstPage': + PDFView.page = 1; + break; + + default: + break; // No action according to spec + } + }, + getDestinationHash: function pdfViewGetDestinationHash(dest) { if (typeof dest === 'string') { return PDFView.getAnchorUrl('#' + escape(dest)); @@ -946,7 +886,6 @@ var PDFView = { this.pageRotation = 0; - var pages = this.pages = []; var pagesRefMap = this.pagesRefMap = {}; var resolvePagesPromise; @@ -956,7 +895,7 @@ var PDFView = { this.pagesPromise = pagesPromise; var firstPagePromise = pdfDocument.getPage(1); - var container = document.getElementById('viewer'); + var pagesViewer = this.pdfViewer; var thumbsViewer = this.pdfThumbnailViewer; // Fetch a single page so we can get a viewport that will be the default @@ -965,12 +904,9 @@ var PDFView = { var viewport = pdfPage.getViewport((scale || 1.0) * CSS_UNITS); for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { var viewportClone = viewport.clone(); - var pageView = new PageView(container, pageNum, scale, - self.navigateTo.bind(self), - viewportClone); + var pageView = pagesViewer.addPage(pageNum, scale, viewportClone); var thumbnailView = thumbsViewer.addThumbnail(pageNum, viewportClone); bindOnAfterDraw(pageView, thumbnailView); - pages.push(pageView); } // Fetch all the pages since the viewport is needed before printing @@ -981,7 +917,7 @@ var PDFView = { var getPagesLeft = pagesCount; for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) { - var pageView = pages[pageNum - 1]; + var pageView = PDFView.getPageView(pageNum - 1); if (!pageView.pdfPage) { pageView.setPdfPage(pdfPage); } @@ -1005,7 +941,7 @@ var PDFView = { window.dispatchEvent(event); }); - PDFView.loadingBar.setWidth(container); + PDFView.loadingBar.setWidth(document.getElementById('viewer')); PDFView.findController.resolveFirstPage(); @@ -1031,7 +967,8 @@ var PDFView = { var storedHash = null; if (showPreviousViewOnLoad && store.get('exists', false)) { var pageNum = store.get('page', '1'); - var zoom = defaultZoomValue || store.get('zoom', PDFView.currentScale); + var zoom = defaultZoomValue || + store.get('zoom', PDFView.pdfViewer.currentScale); var left = store.get('scrollLeft', '0'); var top = store.get('scrollTop', '0'); @@ -1045,9 +982,9 @@ var PDFView = { // Make all navigation keys work on document load, // unless the viewer is embedded in a web page. if (!self.isViewerEmbedded) { - self.container.focus(); + self.pdfViewer.focus(); //#if (FIREFOX || MOZCENTRAL) -// self.container.blur(); +// self.pdfViewer.blur(); //#endif } }, function rejected(reason) { @@ -1183,15 +1120,13 @@ var PDFView = { setInitialView: function pdfViewSetInitialView(storedHash, scale) { // Reset the current scale, as otherwise the page's scale might not get // updated if the zoom level stayed the same. - this.currentScale = UNKNOWN_SCALE; - this.currentScaleValue = null; + this.pdfViewer.resetView(); + // When opening a new file (when one is already loaded in the viewer): // Reset 'currentPageNumber', since otherwise the page's scale will be wrong // if 'currentPageNumber' is larger than the number of pages in the file. - document.getElementById('pageNumber').value = currentPageNumber = 1; - // Reset the current position when loading a new file, - // to prevent displaying the wrong position in the document. - this.currentPosition = null; + document.getElementById('pageNumber').value = + this.pdfViewer.currentPageNumber = 1; if (PDFHistory.initialDestination) { this.navigateTo(PDFHistory.initialDestination); @@ -1203,14 +1138,14 @@ var PDFView = { } else if (storedHash) { this.setHash(storedHash); } else if (scale) { - this.setScale(scale, true); + this.pdfViewer.setScale(scale, true); this.page = 1; } - if (PDFView.currentScale === UNKNOWN_SCALE) { + if (PDFView.pdfViewer.currentScale === UNKNOWN_SCALE) { // Scale was not initialized: invalid bookmark or scale was not specified. // Setting the default one. - this.setScale(DEFAULT_SCALE, true); + this.pdfViewer.setScale(DEFAULT_SCALE, true); } }, @@ -1222,11 +1157,7 @@ var PDFView = { } // Pages have a higher priority than thumbnails, so check them first. - var visiblePages = currentlyVisiblePages || this.getVisiblePages(); - var pageView = this.getHighestPriority(visiblePages, this.pages, - this.pageViewScroll.down); - if (pageView) { - this.renderView(pageView, 'page'); + if (this.pdfViewer.forceRendering(currentlyVisiblePages)) { return; } // No pages needed rendering so check thumbnails. @@ -1247,15 +1178,9 @@ var PDFView = { }, cleanup: function pdfViewCleanup() { - for (var i = 0, ii = this.pages.length; i < ii; i++) { - if (this.pages[i] && - this.pages[i].renderingState !== RenderingStates.FINISHED) { - this.pages[i].reset(); - } - } - this.pdfDocument.cleanup(); - + this.pdfViewer.cleanup(); this.pdfThumbnailViewer.cleanup(); + this.pdfDocument.cleanup(); }, getHighestPriority: function pdfViewGetHighestPriority(visible, views, @@ -1363,7 +1288,7 @@ var PDFView = { zoomArg]; } if (dest) { - var currentPage = this.pages[(pageNumber || this.page) - 1]; + var currentPage = this.getPageView((pageNumber || this.page) - 1); currentPage.scrollIntoView(dest); } else if (pageNumber) { this.page = pageNumber; // simple page @@ -1443,19 +1368,6 @@ var PDFView = { } }, - getVisiblePages: function pdfViewGetVisiblePages() { - if (!PresentationMode.active) { - return getVisibleElements(this.container, this.pages, true); - } else { - // The algorithm in getVisibleElements doesn't work in all browsers and - // configurations when presentation mode is active. - var visible = []; - var currentPage = this.pages[this.page - 1]; - visible.push({ id: currentPage.id, view: currentPage }); - return { first: currentPage, last: currentPage, views: visible }; - } - }, - // Helper function to parse query string (e.g. ?param1=value&parm2=...). parseQueryString: function pdfViewParseQueryString(query) { var parts = query.split('&'); @@ -1479,11 +1391,11 @@ var PDFView = { var alertNotReady = false; var i, ii; - if (!this.pages.length) { + if (!this.pagesCount) { alertNotReady = true; } else { - for (i = 0, ii = this.pages.length; i < ii; ++i) { - if (!this.pages[i].pdfPage) { + for (i = 0, ii = this.pagesCount; i < ii; ++i) { + if (!this.getPageView(i).pdfPage) { alertNotReady = true; break; } @@ -1501,8 +1413,8 @@ var PDFView = { var body = document.querySelector('body'); body.setAttribute('data-mozPrintCallback', true); - for (i = 0, ii = this.pages.length; i < ii; ++i) { - this.pages[i].beforePrint(); + for (i = 0, ii = this.pagesCount; i < ii; ++i) { + this.getPageView(i).beforePrint(); } //#if (FIREFOX || MOZCENTRAL) @@ -1522,20 +1434,17 @@ var PDFView = { this.renderHighestPriority(); }, + setScale: function (value, resetAutoSettings, noScroll) { + this.pdfViewer.setScale(value, resetAutoSettings, noScroll); + }, + rotatePages: function pdfViewRotatePages(delta) { - var currentPage = this.pages[this.page - 1]; - var i, l; + var currentPage = this.getPageView(this.page - 1); + this.pageRotation = (this.pageRotation + 360 + delta) % 360; - - for (i = 0, l = this.pages.length; i < l; i++) { - var page = this.pages[i]; - page.update(page.scale, this.pageRotation); - } - + this.pdfViewer.updateRotation(this.pageRotation); this.pdfThumbnailViewer.updateRotation(this.pageRotation); - this.setScale(this.currentScaleValue, true, true); - this.renderHighestPriority(); if (currentPage) { @@ -1590,7 +1499,7 @@ var PDFView = { // In case we are already on the first or the last page there is no need // to do anything. if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) || - (currentPage === this.pages.length && + (currentPage === this.pagesCount && pageFlipDirection === PageFlipDirection.DOWN)) { return; } @@ -1613,6 +1522,7 @@ var PDFView = { }; //#include page_view.js +//#include pdf_viewer.js //#include thumbnail_view.js //#include text_layer_builder.js //#include document_outline_view.js @@ -1847,7 +1757,7 @@ function webViewerInitialized() { document.getElementById('scaleSelect').addEventListener('change', function() { - PDFView.setScale(this.value); + PDFView.pdfViewer.setScale(this.value); }); document.getElementById('presentationMode').addEventListener('click', @@ -1905,100 +1815,49 @@ function webViewerInitialized() { document.addEventListener('DOMContentLoaded', webViewerLoad, true); function updateViewarea() { - if (!PDFView.initialized) { return; } - var visible = PDFView.getVisiblePages(); - var visiblePages = visible.views; - if (visiblePages.length === 0) { + PDFView.pdfViewer.update(); +} + +window.addEventListener('updateviewarea', function () { + if (!PDFView.initialized) { return; } - var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, - 2 * visiblePages.length + 1); - cache.resize(suggestedCacheSize); - - PDFView.renderHighestPriority(visible); - - var currentId = PDFView.page; - var firstPage = visible.first; - - for (var i = 0, ii = visiblePages.length, stillFullyVisible = false; - i < ii; ++i) { - var page = visiblePages[i]; - - if (page.percent < 100) { - break; - } - if (page.id === PDFView.page) { - stillFullyVisible = true; - break; - } - } - - if (!stillFullyVisible) { - currentId = visiblePages[0].id; - } - - if (!PresentationMode.active) { - updateViewarea.inProgress = true; // used in "set page" - PDFView.page = currentId; - updateViewarea.inProgress = false; - } - - var currentScale = PDFView.currentScale; - var currentScaleValue = PDFView.currentScaleValue; - var normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? - Math.round(currentScale * 10000) / 100 : currentScaleValue; - - var pageNumber = firstPage.id; - var pdfOpenParams = '#page=' + pageNumber; - pdfOpenParams += '&zoom=' + normalizedScaleValue; - var currentPage = PDFView.pages[pageNumber - 1]; - var container = PDFView.container; - var topLeft = currentPage.getPagePoint((container.scrollLeft - firstPage.x), - (container.scrollTop - firstPage.y)); - var intLeft = Math.round(topLeft[0]); - var intTop = Math.round(topLeft[1]); - pdfOpenParams += ',' + intLeft + ',' + intTop; - - if (PresentationMode.active || PresentationMode.switchInProgress) { - PDFView.currentPosition = null; - } else { - PDFView.currentPosition = { page: pageNumber, left: intLeft, top: intTop }; - } + var location = PDFView.pdfViewer.location; PDFView.store.initializedPromise.then(function() { PDFView.store.setMultiple({ 'exists': true, - 'page': pageNumber, - 'zoom': normalizedScaleValue, - 'scrollLeft': intLeft, - 'scrollTop': intTop + 'page': location.pageNumber, + 'zoom': location.scale, + 'scrollLeft': location.left, + 'scrollTop': location.top }).catch(function() { // unable to write to storage }); }); - var href = PDFView.getAnchorUrl(pdfOpenParams); + var href = PDFView.getAnchorUrl(location.pdfOpenParams); document.getElementById('viewBookmark').href = href; document.getElementById('secondaryViewBookmark').href = href; // Update the current bookmark in the browsing history. - PDFHistory.updateCurrentBookmark(pdfOpenParams, pageNumber); -} + PDFHistory.updateCurrentBookmark(location.pdfOpenParams, location.pageNumber); +}, true); window.addEventListener('resize', function webViewerResize(evt) { if (PDFView.initialized && (document.getElementById('pageWidthOption').selected || document.getElementById('pageFitOption').selected || document.getElementById('pageAutoOption').selected)) { - PDFView.setScale(document.getElementById('scaleSelect').value); + PDFView.pdfViewer.setScale(document.getElementById('scaleSelect').value); } updateViewarea(); // Set the 'max-height' CSS property of the secondary toolbar. - SecondaryToolbar.setMaxHeight(PDFView.container); + SecondaryToolbar.setMaxHeight(document.getElementById('viewerContainer')); }); window.addEventListener('hashchange', function webViewerHashchange(evt) { @@ -2077,7 +1936,7 @@ window.addEventListener('localized', function localized(evt) { } // Set the 'max-height' CSS property of the secondary toolbar. - SecondaryToolbar.setMaxHeight(PDFView.container); + SecondaryToolbar.setMaxHeight(document.getElementById('viewerContainer')); }); }, true); @@ -2096,6 +1955,12 @@ window.addEventListener('scalechange', function scalechange(evt) { return; } + if (evt.presetValue) { + selectScaleOption(evt.presetValue); + updateViewarea(); + return; + } + var predefinedValueFound = selectScaleOption('' + evt.scale); if (!predefinedValueFound) { customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; @@ -2106,17 +1971,27 @@ window.addEventListener('scalechange', function scalechange(evt) { window.addEventListener('pagechange', function pagechange(evt) { var page = evt.pageNumber; - if (PDFView.previousPageNumber !== page) { + if (PDFView.pdfViewer.previousPageNumber !== page) { document.getElementById('pageNumber').value = page; PDFView.pdfThumbnailViewer.updatePage(page); } - var numPages = PDFView.pages.length; + var numPages = PDFView.pagesCount; document.getElementById('previous').disabled = (page <= 1); document.getElementById('next').disabled = (page >= numPages); document.getElementById('firstPage').disabled = (page <= 1); document.getElementById('lastPage').disabled = (page >= numPages); + + // checking if the this.page was called from the updateViewarea function + if (evt.updateInProgress) { + return; + } + // Avoid scrolling the first page during loading + if (this.loading && page === 1) { + return; + } + PDFView.getPageView(page - 1).scrollIntoView(); }, true); function handleMouseWheel(evt) { @@ -2138,7 +2013,8 @@ window.addEventListener('mousewheel', handleMouseWheel); window.addEventListener('click', function click(evt) { if (!PresentationMode.active) { - if (SecondaryToolbar.opened && PDFView.container.contains(evt.target)) { + if (SecondaryToolbar.opened && + PDFView.pdfViewer.containsElement(evt.target)) { SecondaryToolbar.close(); } } else if (evt.button === 0) { @@ -2194,7 +2070,7 @@ window.addEventListener('keydown', function keydown(evt) { // keeping it unhandled (to restore page zoom to 100%) setTimeout(function () { // ... and resetting the scale after browser adjusts its scale - PDFView.setScale(DEFAULT_SCALE, true); + PDFView.pdfViewer.setScale(DEFAULT_SCALE, true); }); handled = false; break; @@ -2259,7 +2135,7 @@ window.addEventListener('keydown', function keydown(evt) { /* falls through */ case 37: // left arrow // horizontal scrolling using arrow keys - if (PDFView.isHorizontalScrollbarEnabled) { + if (PDFView.pdfViewer.isHorizontalScrollbarEnabled) { break; } /* falls through */ @@ -2288,7 +2164,7 @@ window.addEventListener('keydown', function keydown(evt) { /* falls through */ case 39: // right arrow // horizontal scrolling using arrow keys - if (PDFView.isHorizontalScrollbarEnabled) { + if (PDFView.pdfViewer.isHorizontalScrollbarEnabled) { break; } /* falls through */ @@ -2306,8 +2182,8 @@ window.addEventListener('keydown', function keydown(evt) { break; case 35: // end if (PresentationMode.active || (PDFView.pdfDocument && - PDFView.page < PDFView.pdfDocument.numPages)) { - PDFView.page = PDFView.pdfDocument.numPages; + PDFView.page < PDFView.pagesCount)) { + PDFView.page = PDFView.pagesCount; handled = true; } break; @@ -2344,21 +2220,21 @@ window.addEventListener('keydown', function keydown(evt) { // 33=Page Up 34=Page Down 35=End 36=Home // 37=Left 38=Up 39=Right 40=Down if (evt.keyCode >= 33 && evt.keyCode <= 40 && - !PDFView.container.contains(curElement)) { + !PDFView.pdfViewer.containsElement(curElement)) { // The page container is not focused, but a page navigation key has been // pressed. Change the focus to the viewer container to make sure that // navigation by keyboard works as expected. - PDFView.container.focus(); + PDFView.pdfViewer.focus(); } // 32=Spacebar if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') { //#if (FIREFOX || MOZCENTRAL) // // Workaround for issue in Firefox, that prevents scroll keys from // // working when elements with 'tabindex' are focused. (#3498) -// PDFView.container.blur(); +// PDFView.pdfViewer.blur(); //#else - if (!PDFView.container.contains(curElement)) { - PDFView.container.focus(); + if (!PDFView.pdfViewer.containsElement(curElement)) { + PDFView.pdfViewer.focus(); } //#endif }