Skip to content

Commit 67a1dfc

Browse files
committed
Move the sidebar related code from viewer.js into PDFSidebar
The sidebar code has, except for minor fixes/additions (such as attachments), been largely untouch for years. To avoid having a bunch of sidebar code sprinkled throughout viewer.js, this patch moves the sidebar code into a separate file (pdf_sidebar.js), similar to how most other functionality has been moved in the last few years. Besides simply moving code around, this patch also has the added benefit that we now keep track of the sidebar state (not just opened/closed). This now makes it possible to handle both `Preferences` *and* `ViewHistory` settings for the sidebar state in a cleaner way, preventing strange and confusing interactions between the two.
1 parent 21f0482 commit 67a1dfc

File tree

4 files changed

+379
-162
lines changed

4 files changed

+379
-162
lines changed

web/pdf_sidebar.js

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
/* Copyright 2016 Mozilla Foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
/* globals RenderingStates */
16+
17+
'use strict';
18+
19+
var SidebarView = {
20+
NONE: 0,
21+
THUMBS: 1,
22+
OUTLINE: 2,
23+
ATTACHMENTS: 3
24+
};
25+
26+
/**
27+
* @typedef {Object} PDFSidebarOptions
28+
* @property {PDFViewer} - The document viewer.
29+
* @property {PDFThumbnailViewer} - The thumbnail viewer.
30+
* @property {PDFOutlineViewer} - The outline viewer.
31+
* @property {HTMLDivElement} mainContainer - The main container
32+
* (in which the viewer element is placed).
33+
* @property {HTMLDivElement} outerContainer - The outer container
34+
* (encasing both the viewer and sidebar elements).
35+
* @property {HTMLButtonElement} toggleButton - The button used for
36+
* opening/closing the sidebar.
37+
* @property {HTMLButtonElement} thumbnailButton - The button used to show
38+
* the thumbnail view.
39+
* @property {HTMLButtonElement} outlineButton - The button used to show
40+
* the outline view.
41+
* @property {HTMLButtonElement} attachmentsButton - The button used to show
42+
* the attachments view.
43+
* @property {HTMLDivElement} thumbnailView - The container in which
44+
* the thumbnails are placed.
45+
* @property {HTMLDivElement} outlineView - The container in which
46+
* the outline is placed.
47+
* @property {HTMLDivElement} attachmentsView - The container in which
48+
* the attachments are placed.
49+
*/
50+
51+
/**
52+
* @class
53+
*/
54+
var PDFSidebar = (function PDFSidebarClosure() {
55+
/**
56+
* @constructs PDFSidebar
57+
* @param {PDFSidebarOptions} options
58+
*/
59+
function PDFSidebar(options) {
60+
this.isOpen = false;
61+
this.active = SidebarView.THUMBS;
62+
this.isInitialViewSet = false;
63+
64+
/**
65+
* Callback used when the sidebar has been opened/closed, to ensure that
66+
* the viewers (PDFViewer/PDFThumbnailViewer) are updated correctly.
67+
*/
68+
this.onToggled = null;
69+
70+
this.pdfViewer = options.pdfViewer;
71+
this.pdfThumbnailViewer = options.pdfThumbnailViewer;
72+
this.pdfOutlineViewer = options.pdfOutlineViewer;
73+
74+
this.mainContainer = options.mainContainer;
75+
this.outerContainer = options.outerContainer;
76+
this.toggleButton = options.toggleButton;
77+
78+
this.thumbnailButton = options.thumbnailButton;
79+
this.outlineButton = options.outlineButton;
80+
this.attachmentsButton = options.attachmentsButton;
81+
82+
this.thumbnailView = options.thumbnailView;
83+
this.outlineView = options.outlineView;
84+
this.attachmentsView = options.attachmentsView;
85+
86+
this._addEventListeners();
87+
}
88+
89+
PDFSidebar.prototype = {
90+
reset: function PDFSidebar_reset() {
91+
this.isInitialViewSet = false;
92+
93+
this.close();
94+
this.switchView(SidebarView.THUMBS);
95+
96+
this.outlineButton.disabled = false;
97+
this.attachmentsButton.disabled = false;
98+
},
99+
100+
/**
101+
* @returns {number} One of the values in {SidebarView}.
102+
*/
103+
get visibleView() {
104+
return (this.isOpen ? this.active : SidebarView.NONE);
105+
},
106+
107+
get isThumbnailViewVisible() {
108+
return (this.isOpen && this.active === SidebarView.THUMBS);
109+
},
110+
111+
get isOutlineViewVisible() {
112+
return (this.isOpen && this.active === SidebarView.OUTLINE);
113+
},
114+
115+
get isAttachmentsViewVisible() {
116+
return (this.isOpen && this.active === SidebarView.ATTACHMENTS);
117+
},
118+
119+
/**
120+
* @param {number} view - The sidebar view that should become visible,
121+
* must be one of the values in {SidebarView}.
122+
*/
123+
setInitialView: function PDFSidebar_setInitialView(view) {
124+
if (this.isInitialViewSet) {
125+
return;
126+
}
127+
this.isInitialViewSet = true;
128+
129+
if (this.isOpen && view === SidebarView.NONE) {
130+
// If the user has already manually opened the sidebar,
131+
// immediately closing it would be bad UX.
132+
return;
133+
}
134+
this.switchView(view, true);
135+
},
136+
137+
/**
138+
* @param {number} view - The sidebar view that should be switched to,
139+
* must be one of the values in {SidebarView}.
140+
* @param {boolean} forceOpen - Ensure that the sidebar is opened.
141+
* The default value is false.
142+
*/
143+
switchView: function PDFSidebar_switchView(view, forceOpen) {
144+
if (view === SidebarView.NONE) {
145+
this.close();
146+
return;
147+
}
148+
if (forceOpen) {
149+
this.open();
150+
}
151+
var shouldForceRendering = false;
152+
153+
switch (view) {
154+
case SidebarView.THUMBS:
155+
this.thumbnailButton.classList.add('toggled');
156+
this.outlineButton.classList.remove('toggled');
157+
this.attachmentsButton.classList.remove('toggled');
158+
159+
this.thumbnailView.classList.remove('hidden');
160+
this.outlineView.classList.add('hidden');
161+
this.attachmentsView.classList.add('hidden');
162+
163+
if (this.isOpen && view !== this.active) {
164+
this._updateThumbnailViewer();
165+
shouldForceRendering = true;
166+
}
167+
break;
168+
case SidebarView.OUTLINE:
169+
if (this.outlineButton.disabled) {
170+
return;
171+
}
172+
this.thumbnailButton.classList.remove('toggled');
173+
this.outlineButton.classList.add('toggled');
174+
this.attachmentsButton.classList.remove('toggled');
175+
176+
this.thumbnailView.classList.add('hidden');
177+
this.outlineView.classList.remove('hidden');
178+
this.attachmentsView.classList.add('hidden');
179+
break;
180+
case SidebarView.ATTACHMENTS:
181+
if (this.attachmentsButton.disabled) {
182+
return;
183+
}
184+
this.thumbnailButton.classList.remove('toggled');
185+
this.outlineButton.classList.remove('toggled');
186+
this.attachmentsButton.classList.add('toggled');
187+
188+
this.thumbnailView.classList.add('hidden');
189+
this.outlineView.classList.add('hidden');
190+
this.attachmentsView.classList.remove('hidden');
191+
break;
192+
default:
193+
console.error('PDFSidebar_switchView: "' + view +
194+
'" is an unsupported value.');
195+
return;
196+
}
197+
// Update the active view *after* it has been validated above,
198+
// in order to prevent setting it to an invalid state.
199+
this.active = view | 0;
200+
201+
if (shouldForceRendering) {
202+
this._forceRendering();
203+
}
204+
},
205+
206+
open: function PDFSidebar_open() {
207+
if (this.isOpen) {
208+
return;
209+
}
210+
this.isOpen = true;
211+
this.toggleButton.classList.add('toggled');
212+
213+
this.outerContainer.classList.add('sidebarMoving');
214+
this.outerContainer.classList.add('sidebarOpen');
215+
216+
if (this.active === SidebarView.THUMBS) {
217+
this._updateThumbnailViewer();
218+
}
219+
this._forceRendering();
220+
},
221+
222+
close: function PDFSidebar_close() {
223+
if (!this.isOpen) {
224+
return;
225+
}
226+
this.isOpen = false;
227+
this.toggleButton.classList.remove('toggled');
228+
229+
this.outerContainer.classList.add('sidebarMoving');
230+
this.outerContainer.classList.remove('sidebarOpen');
231+
232+
this._forceRendering();
233+
},
234+
235+
toggle: function PDFSidebar_toggle() {
236+
if (this.isOpen) {
237+
this.close();
238+
} else {
239+
this.open();
240+
}
241+
},
242+
243+
/**
244+
* @private
245+
*/
246+
_forceRendering: function PDFSidebar_forceRendering() {
247+
if (this.onToggled) {
248+
this.onToggled();
249+
} else { // Fallback
250+
this.pdfViewer.forceRendering();
251+
this.pdfThumbnailViewer.forceRendering();
252+
}
253+
},
254+
255+
/**
256+
* @private
257+
*/
258+
_updateThumbnailViewer: function PDFSidebar_updateThumbnailViewer() {
259+
var pdfViewer = this.pdfViewer;
260+
var thumbnailViewer = this.pdfThumbnailViewer;
261+
262+
// Use the rendered pages to set the corresponding thumbnail images.
263+
var pagesCount = pdfViewer.pagesCount;
264+
for (var pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
265+
var pageView = pdfViewer.getPageView(pageIndex);
266+
if (pageView && pageView.renderingState === RenderingStates.FINISHED) {
267+
var thumbnailView = thumbnailViewer.getThumbnail(pageIndex);
268+
thumbnailView.setImage(pageView);
269+
}
270+
}
271+
thumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
272+
},
273+
274+
/**
275+
* @private
276+
*/
277+
_addEventListeners: function PDFSidebar_addEventListeners() {
278+
var self = this;
279+
280+
self.mainContainer.addEventListener('transitionend', function(evt) {
281+
if (evt.target === /* mainContainer */ this) {
282+
self.outerContainer.classList.remove('sidebarMoving');
283+
}
284+
});
285+
286+
// Buttons for switching views.
287+
self.thumbnailButton.addEventListener('click', function() {
288+
self.switchView(SidebarView.THUMBS);
289+
});
290+
291+
self.outlineButton.addEventListener('click', function() {
292+
self.switchView(SidebarView.OUTLINE);
293+
});
294+
self.outlineButton.addEventListener('dblclick', function() {
295+
self.pdfOutlineViewer.toggleOutlineTree();
296+
});
297+
298+
self.attachmentsButton.addEventListener('click', function() {
299+
self.switchView(SidebarView.ATTACHMENTS);
300+
});
301+
302+
// Disable/enable views.
303+
self.outlineView.addEventListener('outlineloaded', function(evt) {
304+
var outlineCount = evt.detail.outlineCount;
305+
306+
self.outlineButton.disabled = !outlineCount;
307+
if (!outlineCount && self.active === SidebarView.OUTLINE) {
308+
self.switchView(SidebarView.THUMBS);
309+
}
310+
});
311+
312+
self.attachmentsView.addEventListener('attachmentsloaded', function(evt) {
313+
var attachmentsCount = evt.detail.attachmentsCount;
314+
315+
self.attachmentsButton.disabled = !attachmentsCount;
316+
if (!attachmentsCount && self.active === SidebarView.ATTACHMENTS) {
317+
self.switchView(SidebarView.THUMBS);
318+
}
319+
});
320+
},
321+
};
322+
323+
return PDFSidebar;
324+
})();

web/preferences.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,6 @@
1818

1919
//#include default_preferences.js
2020

21-
var SidebarView = {
22-
NONE: 0,
23-
THUMBS: 1,
24-
OUTLINE: 2,
25-
ATTACHMENTS: 3
26-
};
27-
2821
/**
2922
* Preferences - Utility for storing persistent settings.
3023
* Used for settings that should be applied to all opened documents,

web/viewer.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<script src="pdf_viewer.js"></script>
7575
<script src="pdf_thumbnail_view.js"></script>
7676
<script src="pdf_thumbnail_viewer.js"></script>
77+
<script src="pdf_sidebar.js"></script>
7778
<script src="pdf_outline_viewer.js"></script>
7879
<script src="pdf_attachment_viewer.js"></script>
7980
<script src="pdf_find_bar.js"></script>

0 commit comments

Comments
 (0)