diff --git a/web/pdf_sidebar.js b/web/pdf_sidebar.js
new file mode 100644
index 000000000..0490ef249
--- /dev/null
+++ b/web/pdf_sidebar.js
@@ -0,0 +1,324 @@
+/* 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.
+ */
+ /* globals RenderingStates */
+
+'use strict';
+
+var SidebarView = {
+ NONE: 0,
+ THUMBS: 1,
+ OUTLINE: 2,
+ ATTACHMENTS: 3
+};
+
+/**
+ * @typedef {Object} PDFSidebarOptions
+ * @property {PDFViewer} - The document viewer.
+ * @property {PDFThumbnailViewer} - The thumbnail viewer.
+ * @property {PDFOutlineViewer} - The outline viewer.
+ * @property {HTMLDivElement} mainContainer - The main container
+ * (in which the viewer element is placed).
+ * @property {HTMLDivElement} outerContainer - The outer container
+ * (encasing both the viewer and sidebar elements).
+ * @property {HTMLButtonElement} toggleButton - The button used for
+ * opening/closing the sidebar.
+ * @property {HTMLButtonElement} thumbnailButton - The button used to show
+ * the thumbnail view.
+ * @property {HTMLButtonElement} outlineButton - The button used to show
+ * the outline view.
+ * @property {HTMLButtonElement} attachmentsButton - The button used to show
+ * the attachments view.
+ * @property {HTMLDivElement} thumbnailView - The container in which
+ * the thumbnails are placed.
+ * @property {HTMLDivElement} outlineView - The container in which
+ * the outline is placed.
+ * @property {HTMLDivElement} attachmentsView - The container in which
+ * the attachments are placed.
+ */
+
+/**
+ * @class
+ */
+var PDFSidebar = (function PDFSidebarClosure() {
+ /**
+ * @constructs PDFSidebar
+ * @param {PDFSidebarOptions} options
+ */
+ function PDFSidebar(options) {
+ this.isOpen = false;
+ this.active = SidebarView.THUMBS;
+ this.isInitialViewSet = false;
+
+ /**
+ * Callback used when the sidebar has been opened/closed, to ensure that
+ * the viewers (PDFViewer/PDFThumbnailViewer) are updated correctly.
+ */
+ this.onToggled = null;
+
+ this.pdfViewer = options.pdfViewer;
+ this.pdfThumbnailViewer = options.pdfThumbnailViewer;
+ this.pdfOutlineViewer = options.pdfOutlineViewer;
+
+ this.mainContainer = options.mainContainer;
+ this.outerContainer = options.outerContainer;
+ this.toggleButton = options.toggleButton;
+
+ this.thumbnailButton = options.thumbnailButton;
+ this.outlineButton = options.outlineButton;
+ this.attachmentsButton = options.attachmentsButton;
+
+ this.thumbnailView = options.thumbnailView;
+ this.outlineView = options.outlineView;
+ this.attachmentsView = options.attachmentsView;
+
+ this._addEventListeners();
+ }
+
+ PDFSidebar.prototype = {
+ reset: function PDFSidebar_reset() {
+ this.isInitialViewSet = false;
+
+ this.close();
+ this.switchView(SidebarView.THUMBS);
+
+ this.outlineButton.disabled = false;
+ this.attachmentsButton.disabled = false;
+ },
+
+ /**
+ * @returns {number} One of the values in {SidebarView}.
+ */
+ get visibleView() {
+ return (this.isOpen ? this.active : SidebarView.NONE);
+ },
+
+ get isThumbnailViewVisible() {
+ return (this.isOpen && this.active === SidebarView.THUMBS);
+ },
+
+ get isOutlineViewVisible() {
+ return (this.isOpen && this.active === SidebarView.OUTLINE);
+ },
+
+ get isAttachmentsViewVisible() {
+ return (this.isOpen && this.active === SidebarView.ATTACHMENTS);
+ },
+
+ /**
+ * @param {number} view - The sidebar view that should become visible,
+ * must be one of the values in {SidebarView}.
+ */
+ setInitialView: function PDFSidebar_setInitialView(view) {
+ if (this.isInitialViewSet) {
+ return;
+ }
+ this.isInitialViewSet = true;
+
+ if (this.isOpen && view === SidebarView.NONE) {
+ // If the user has already manually opened the sidebar,
+ // immediately closing it would be bad UX.
+ return;
+ }
+ this.switchView(view, true);
+ },
+
+ /**
+ * @param {number} view - The sidebar view that should be switched to,
+ * must be one of the values in {SidebarView}.
+ * @param {boolean} forceOpen - Ensure that the sidebar is opened.
+ * The default value is false.
+ */
+ switchView: function PDFSidebar_switchView(view, forceOpen) {
+ if (view === SidebarView.NONE) {
+ this.close();
+ return;
+ }
+ if (forceOpen) {
+ this.open();
+ }
+ var shouldForceRendering = false;
+
+ switch (view) {
+ case SidebarView.THUMBS:
+ this.thumbnailButton.classList.add('toggled');
+ this.outlineButton.classList.remove('toggled');
+ this.attachmentsButton.classList.remove('toggled');
+
+ this.thumbnailView.classList.remove('hidden');
+ this.outlineView.classList.add('hidden');
+ this.attachmentsView.classList.add('hidden');
+
+ if (this.isOpen && view !== this.active) {
+ this._updateThumbnailViewer();
+ shouldForceRendering = true;
+ }
+ break;
+ case SidebarView.OUTLINE:
+ if (this.outlineButton.disabled) {
+ return;
+ }
+ this.thumbnailButton.classList.remove('toggled');
+ this.outlineButton.classList.add('toggled');
+ this.attachmentsButton.classList.remove('toggled');
+
+ this.thumbnailView.classList.add('hidden');
+ this.outlineView.classList.remove('hidden');
+ this.attachmentsView.classList.add('hidden');
+ break;
+ case SidebarView.ATTACHMENTS:
+ if (this.attachmentsButton.disabled) {
+ return;
+ }
+ this.thumbnailButton.classList.remove('toggled');
+ this.outlineButton.classList.remove('toggled');
+ this.attachmentsButton.classList.add('toggled');
+
+ this.thumbnailView.classList.add('hidden');
+ this.outlineView.classList.add('hidden');
+ this.attachmentsView.classList.remove('hidden');
+ break;
+ default:
+ console.error('PDFSidebar_switchView: "' + view +
+ '" is an unsupported value.');
+ return;
+ }
+ // Update the active view *after* it has been validated above,
+ // in order to prevent setting it to an invalid state.
+ this.active = view | 0;
+
+ if (shouldForceRendering) {
+ this._forceRendering();
+ }
+ },
+
+ open: function PDFSidebar_open() {
+ if (this.isOpen) {
+ return;
+ }
+ this.isOpen = true;
+ this.toggleButton.classList.add('toggled');
+
+ this.outerContainer.classList.add('sidebarMoving');
+ this.outerContainer.classList.add('sidebarOpen');
+
+ if (this.active === SidebarView.THUMBS) {
+ this._updateThumbnailViewer();
+ }
+ this._forceRendering();
+ },
+
+ close: function PDFSidebar_close() {
+ if (!this.isOpen) {
+ return;
+ }
+ this.isOpen = false;
+ this.toggleButton.classList.remove('toggled');
+
+ this.outerContainer.classList.add('sidebarMoving');
+ this.outerContainer.classList.remove('sidebarOpen');
+
+ this._forceRendering();
+ },
+
+ toggle: function PDFSidebar_toggle() {
+ if (this.isOpen) {
+ this.close();
+ } else {
+ this.open();
+ }
+ },
+
+ /**
+ * @private
+ */
+ _forceRendering: function PDFSidebar_forceRendering() {
+ if (this.onToggled) {
+ this.onToggled();
+ } else { // Fallback
+ this.pdfViewer.forceRendering();
+ this.pdfThumbnailViewer.forceRendering();
+ }
+ },
+
+ /**
+ * @private
+ */
+ _updateThumbnailViewer: function PDFSidebar_updateThumbnailViewer() {
+ var pdfViewer = this.pdfViewer;
+ var thumbnailViewer = this.pdfThumbnailViewer;
+
+ // Use the rendered pages to set the corresponding thumbnail images.
+ var pagesCount = pdfViewer.pagesCount;
+ for (var pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
+ var pageView = pdfViewer.getPageView(pageIndex);
+ if (pageView && pageView.renderingState === RenderingStates.FINISHED) {
+ var thumbnailView = thumbnailViewer.getThumbnail(pageIndex);
+ thumbnailView.setImage(pageView);
+ }
+ }
+ thumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
+ },
+
+ /**
+ * @private
+ */
+ _addEventListeners: function PDFSidebar_addEventListeners() {
+ var self = this;
+
+ self.mainContainer.addEventListener('transitionend', function(evt) {
+ if (evt.target === /* mainContainer */ this) {
+ self.outerContainer.classList.remove('sidebarMoving');
+ }
+ });
+
+ // Buttons for switching views.
+ self.thumbnailButton.addEventListener('click', function() {
+ self.switchView(SidebarView.THUMBS);
+ });
+
+ self.outlineButton.addEventListener('click', function() {
+ self.switchView(SidebarView.OUTLINE);
+ });
+ self.outlineButton.addEventListener('dblclick', function() {
+ self.pdfOutlineViewer.toggleOutlineTree();
+ });
+
+ self.attachmentsButton.addEventListener('click', function() {
+ self.switchView(SidebarView.ATTACHMENTS);
+ });
+
+ // Disable/enable views.
+ self.outlineView.addEventListener('outlineloaded', function(evt) {
+ var outlineCount = evt.detail.outlineCount;
+
+ self.outlineButton.disabled = !outlineCount;
+ if (!outlineCount && self.active === SidebarView.OUTLINE) {
+ self.switchView(SidebarView.THUMBS);
+ }
+ });
+
+ self.attachmentsView.addEventListener('attachmentsloaded', function(evt) {
+ var attachmentsCount = evt.detail.attachmentsCount;
+
+ self.attachmentsButton.disabled = !attachmentsCount;
+ if (!attachmentsCount && self.active === SidebarView.ATTACHMENTS) {
+ self.switchView(SidebarView.THUMBS);
+ }
+ });
+ },
+ };
+
+ return PDFSidebar;
+})();
diff --git a/web/preferences.js b/web/preferences.js
index e62b10d21..f0cd46f89 100644
--- a/web/preferences.js
+++ b/web/preferences.js
@@ -18,13 +18,6 @@
//#include default_preferences.js
-var SidebarView = {
- NONE: 0,
- THUMBS: 1,
- OUTLINE: 2,
- ATTACHMENTS: 3
-};
-
/**
* Preferences - Utility for storing persistent settings.
* Used for settings that should be applied to all opened documents,
diff --git a/web/viewer.html b/web/viewer.html
index 9a366e13e..98ee82954 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -74,6 +74,7 @@ See https://github.com/adobe-type-tools/cmap-resources
+
diff --git a/web/viewer.js b/web/viewer.js
index 506725214..1ab9ca30f 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -15,7 +15,7 @@
/* globals PDFJS, PDFBug, FirefoxCom, Stats, ProgressBar, DownloadManager,
getPDFFileNameFromURL, PDFHistory, Preferences, SidebarView,
ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler,
- SecondaryToolbar, PasswordPrompt, PDFPresentationMode,
+ SecondaryToolbar, PasswordPrompt, PDFPresentationMode, PDFSidebar,
PDFDocumentProperties, HandTool, Promise, PDFLinkService,
PDFOutlineViewer, PDFAttachmentViewer, OverlayManager,
PDFFindController, PDFFindBar, PDFViewer, PDFRenderingQueue,
@@ -83,6 +83,7 @@ var mozL10n = document.mozL10n || document.webL10n;
//#include pdf_document_properties.js
//#include pdf_viewer.js
//#include pdf_thumbnail_viewer.js
+//#include pdf_sidebar.js
//#include pdf_outline_viewer.js
//#include pdf_attachment_viewer.js
@@ -93,7 +94,6 @@ var PDFViewerApplication = {
fellback: false,
pdfDocument: null,
pdfLoadingTask: null,
- sidebarOpen: false,
printing: false,
/** @type {PDFViewer} */
pdfViewer: null,
@@ -109,6 +109,8 @@ var PDFViewerApplication = {
pdfLinkService: null,
/** @type {PDFHistory} */
pdfHistory: null,
+ /** @type {PDFSidebar} */
+ pdfSidebar: null,
/** @type {PDFOutlineViewer} */
pdfOutlineViewer: null,
/** @type {PDFAttachmentViewer} */
@@ -259,6 +261,25 @@ var PDFViewerApplication = {
downloadManager: new DownloadManager(),
});
+ this.pdfSidebar = new PDFSidebar({
+ pdfViewer: this.pdfViewer,
+ pdfThumbnailViewer: this.pdfThumbnailViewer,
+ pdfOutlineViewer: this.pdfOutlineViewer,
+ // Divs (and sidebar button)
+ mainContainer: document.getElementById('mainContainer'),
+ outerContainer: document.getElementById('outerContainer'),
+ toggleButton: document.getElementById('sidebarToggle'),
+ // Buttons
+ thumbnailButton: document.getElementById('viewThumbnail'),
+ outlineButton: document.getElementById('viewOutline'),
+ attachmentsButton: document.getElementById('viewAttachments'),
+ // Views
+ thumbnailView: document.getElementById('thumbnailView'),
+ outlineView: document.getElementById('outlineView'),
+ attachmentsView: document.getElementById('attachmentsView'),
+ });
+ this.pdfSidebar.onToggled = this.forceRendering.bind(this);
+
var self = this;
var initializedPromise = Promise.all([
Preferences.get('enableWebGL').then(function resolved(value) {
@@ -540,6 +561,7 @@ var PDFViewerApplication = {
this.pdfLinkService.setDocument(null, null);
}
+ this.pdfSidebar.reset();
this.pdfOutlineViewer.reset();
this.pdfAttachmentViewer.reset();
@@ -968,40 +990,12 @@ var PDFViewerApplication = {
Promise.all(promises).then(function() {
pdfDocument.getOutline().then(function(outline) {
self.pdfOutlineViewer.render({ outline: outline });
-
- var container = document.getElementById('outlineView');
- document.getElementById('viewOutline').disabled = !outline;
-
- if (!outline && !container.classList.contains('hidden')) {
- self.switchSidebarView('thumbs');
- }
- if (outline &&
- self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) {
- self.switchSidebarView('outline', true);
- }
});
pdfDocument.getAttachments().then(function(attachments) {
self.pdfAttachmentViewer.render({ attachments: attachments });
-
- var container = document.getElementById('attachmentsView');
- document.getElementById('viewAttachments').disabled = !attachments;
-
- if (!attachments && !container.classList.contains('hidden')) {
- self.switchSidebarView('thumbs');
- }
- if (attachments &&
- self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) {
- self.switchSidebarView('attachments', true);
- }
});
});
- if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) {
- Promise.all([firstPagePromise, onePageRendered]).then(function () {
- self.switchSidebarView('thumbs', true);
- });
- }
-
pdfDocument.getMetadata().then(function(data) {
var info = data.info, metadata = data.metadata;
self.documentInfo = info;
@@ -1080,6 +1074,8 @@ var PDFViewerApplication = {
document.getElementById('pageNumber').value =
this.pdfViewer.currentPageNumber;
+ this.pdfSidebar.setInitialView(this.preferenceSidebarViewOnLoad);
+
if (this.initialDestination) {
this.pdfLinkService.navigateTo(this.initialDestination);
this.initialDestination = null;
@@ -1112,83 +1108,11 @@ var PDFViewerApplication = {
forceRendering: function pdfViewForceRendering() {
this.pdfRenderingQueue.printing = this.printing;
- this.pdfRenderingQueue.isThumbnailViewEnabled = this.sidebarOpen;
+ this.pdfRenderingQueue.isThumbnailViewEnabled =
+ this.pdfSidebar.isThumbnailViewVisible;
this.pdfRenderingQueue.renderHighestPriority();
},
- refreshThumbnailViewer: function pdfViewRefreshThumbnailViewer() {
- var pdfViewer = this.pdfViewer;
- var thumbnailViewer = this.pdfThumbnailViewer;
-
- // set thumbnail images of rendered pages
- var pagesCount = pdfViewer.pagesCount;
- for (var pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
- var pageView = pdfViewer.getPageView(pageIndex);
- if (pageView && pageView.renderingState === RenderingStates.FINISHED) {
- var thumbnailView = thumbnailViewer.getThumbnail(pageIndex);
- thumbnailView.setImage(pageView);
- }
- }
-
- thumbnailViewer.scrollThumbnailIntoView(this.page);
- },
-
- switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) {
- if (openSidebar && !this.sidebarOpen) {
- document.getElementById('sidebarToggle').click();
- }
- var thumbsView = document.getElementById('thumbnailView');
- var outlineView = document.getElementById('outlineView');
- var attachmentsView = document.getElementById('attachmentsView');
-
- var thumbsButton = document.getElementById('viewThumbnail');
- var outlineButton = document.getElementById('viewOutline');
- var attachmentsButton = document.getElementById('viewAttachments');
-
- switch (view) {
- case 'thumbs':
- var wasAnotherViewVisible = thumbsView.classList.contains('hidden');
-
- thumbsButton.classList.add('toggled');
- outlineButton.classList.remove('toggled');
- attachmentsButton.classList.remove('toggled');
- thumbsView.classList.remove('hidden');
- outlineView.classList.add('hidden');
- attachmentsView.classList.add('hidden');
-
- this.forceRendering();
-
- if (wasAnotherViewVisible) {
- this.pdfThumbnailViewer.ensureThumbnailVisible(this.page);
- }
- break;
-
- case 'outline':
- if (outlineButton.disabled) {
- return;
- }
- thumbsButton.classList.remove('toggled');
- outlineButton.classList.add('toggled');
- attachmentsButton.classList.remove('toggled');
- thumbsView.classList.add('hidden');
- outlineView.classList.remove('hidden');
- attachmentsView.classList.add('hidden');
- break;
-
- case 'attachments':
- if (attachmentsButton.disabled) {
- return;
- }
- thumbsButton.classList.remove('toggled');
- outlineButton.classList.remove('toggled');
- attachmentsButton.classList.add('toggled');
- thumbsView.classList.add('hidden');
- outlineView.classList.add('hidden');
- attachmentsView.classList.remove('hidden');
- break;
- }
- },
-
beforePrint: function pdfViewSetupBeforePrint() {
if (!this.supportsPrinting) {
var printMessage = mozL10n.get('printing_not_supported', null,
@@ -1527,48 +1451,18 @@ function webViewerInitialized() {
// Suppress context menus for some controls
document.getElementById('scaleSelect').oncontextmenu = noContextMenuHandler;
- var mainContainer = document.getElementById('mainContainer');
- var outerContainer = document.getElementById('outerContainer');
- mainContainer.addEventListener('transitionend', function(e) {
- if (e.target === mainContainer) {
- var event = document.createEvent('UIEvents');
- event.initUIEvent('resize', false, false, window, 0);
- window.dispatchEvent(event);
- outerContainer.classList.remove('sidebarMoving');
- }
- }, true);
+ document.getElementById('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);
+ }
+ }, true);
document.getElementById('sidebarToggle').addEventListener('click',
function() {
- this.classList.toggle('toggled');
- outerContainer.classList.add('sidebarMoving');
- outerContainer.classList.toggle('sidebarOpen');
- PDFViewerApplication.sidebarOpen =
- outerContainer.classList.contains('sidebarOpen');
- if (PDFViewerApplication.sidebarOpen) {
- PDFViewerApplication.refreshThumbnailViewer();
- }
- PDFViewerApplication.forceRendering();
- });
-
- document.getElementById('viewThumbnail').addEventListener('click',
- function() {
- PDFViewerApplication.switchSidebarView('thumbs');
- });
-
- document.getElementById('viewOutline').addEventListener('click',
- function() {
- PDFViewerApplication.switchSidebarView('outline');
- });
-
- document.getElementById('viewOutline').addEventListener('dblclick',
- function() {
- PDFViewerApplication.pdfOutlineViewer.toggleOutlineTree();
- });
-
- document.getElementById('viewAttachments').addEventListener('click',
- function() {
- PDFViewerApplication.switchSidebarView('attachments');
+ PDFViewerApplication.pdfSidebar.toggle();
});
document.getElementById('previous').addEventListener('click',
@@ -1668,7 +1562,8 @@ document.addEventListener('pagerendered', function (e) {
var pageIndex = pageNumber - 1;
var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
- if (PDFViewerApplication.sidebarOpen) {
+ // Use the rendered page to set the corresponding thumbnail image.
+ if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
var thumbnailView = PDFViewerApplication.pdfThumbnailViewer.
getThumbnail(pageIndex);
thumbnailView.setImage(pageView);
@@ -1736,23 +1631,26 @@ document.addEventListener('pagemode', function (evt) {
return;
}
// Handle the 'pagemode' hash parameter, see also `PDFLinkService_setHash`.
- var mode = evt.detail.mode;
+ var mode = evt.detail.mode, view;
switch (mode) {
- case 'bookmarks':
- // Note: Our code calls this property 'outline', even though the
- // Open Parameter specification calls it 'bookmarks'.
- mode = 'outline';
- /* falls through */
case 'thumbs':
+ view = SidebarView.THUMBS;
+ break;
+ case 'bookmarks':
+ case 'outline':
+ view = SidebarView.OUTLINE;
+ break;
case 'attachments':
- PDFViewerApplication.switchSidebarView(mode, true);
+ view = SidebarView.ATTACHMENTS;
break;
case 'none':
- if (PDFViewerApplication.sidebarOpen) {
- document.getElementById('sidebarToggle').click();
- }
+ view = SidebarView.NONE;
break;
+ default:
+ console.error('Invalid "pagemode" hash parameter: ' + mode);
+ return;
}
+ PDFViewerApplication.pdfSidebar.switchView(view, /* forceOpen = */ true);
}, true);
document.addEventListener('namedaction', function (e) {
@@ -1954,7 +1852,8 @@ window.addEventListener('pagechange', function pagechange(evt) {
var page = evt.pageNumber;
if (evt.previousPageNumber !== page) {
document.getElementById('pageNumber').value = page;
- if (PDFViewerApplication.sidebarOpen) {
+
+ if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
PDFViewerApplication.pdfThumbnailViewer.scrollThumbnailIntoView(page);
}
}