OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function() { | 5 (function() { |
6 'use strict'; | 6 'use strict'; |
7 | 7 |
8 <include src="../../../../ui/webui/resources/js/util.js"></include> | 8 <include src="../../../../ui/webui/resources/js/util.js"></include> |
9 <include src="viewport.js"></include> | 9 <include src="viewport.js"></include> |
10 | 10 |
11 // The plugin element is sized to fill the entire window and is set to be fixed | 11 /** |
12 // positioning, acting as a viewport. The plugin renders into this viewport | 12 * Creates a new PDFViewer. There should only be one of these objects per |
13 // according to the scroll position of the window. | 13 * document. |
14 var plugin; | 14 */ |
15 | 15 function PDFViewer() { |
16 // This element is placed behind the plugin element to cause scrollbars to be | 16 // The sizer element is placed behind the plugin element to cause scrollbars |
17 // displayed in the window. It is sized according to the document size of the | 17 // to be displayed in the window. It is sized according to the document size |
18 // pdf and zoom level. | 18 // of the pdf and zoom level. |
19 var sizer; | 19 this.sizer_ = $('sizer'); |
20 | 20 this.toolbar_ = $('toolbar'); |
21 // The toolbar element. | 21 this.pageIndicator_ = $('page-indicator'); |
22 var viewerToolbar; | 22 this.progressBar_ = $('progress-bar'); |
23 | 23 this.passwordScreen_ = $('password-screen'); |
24 // The page indicator element. | 24 this.passwordScreen_.addEventListener('password-submitted', |
25 var viewerPageIndicator; | 25 this.onPasswordSubmitted_.bind(this)); |
26 | 26 this.errorScreen_ = $('error-screen'); |
27 // The progress bar element. | 27 this.errorScreen_.text = 'Failed to load PDF document'; |
28 var viewerProgressBar; | |
29 | |
30 // The element indicating there was an error loading the document. | |
31 var viewerErrorScreen; | |
32 | |
33 // The viewport object. | |
34 var viewport; | |
35 | |
36 // The element displaying the password screen. | |
37 var viewerPasswordScreen; | |
38 | |
39 // The document dimensions. | |
40 var documentDimensions; | |
41 | |
42 // Notify the plugin to print. | |
43 function print() { | |
44 plugin.postMessage({ | |
45 type: 'print', | |
46 }); | |
47 } | |
48 | |
49 // Returns true if the fit-to-page button is enabled. | |
50 function isFitToPageEnabled() { | |
51 return $('fit-to-page-button').classList.contains('polymer-selected'); | |
52 } | |
53 | |
54 function updateProgress(progress) { | |
55 viewerProgressBar.progress = progress; | |
56 if (progress == -1) { | |
57 // Document load failed. | |
58 viewerErrorScreen.style.visibility = 'visible'; | |
59 sizer.style.display = 'none'; | |
60 viewerToolbar.style.visibility = 'hidden'; | |
61 if (viewerPasswordScreen.active) { | |
62 viewerPasswordScreen.deny(); | |
63 viewerPasswordScreen.active = false; | |
64 } | |
65 } | |
66 } | |
67 | |
68 function onPasswordSubmitted(event) { | |
69 plugin.postMessage({ | |
70 type: 'getPasswordComplete', | |
71 password: event.detail.password | |
72 }); | |
73 } | |
74 | |
75 // Called when a message is received from the plugin. | |
76 function handleMessage(message) { | |
77 switch (message.data.type.toString()) { | |
78 case 'documentDimensions': | |
79 documentDimensions = message.data; | |
80 viewport.setDocumentDimensions(documentDimensions); | |
81 viewerToolbar.style.visibility = 'visible'; | |
82 // If we received the document dimensions, the password was good so we can | |
83 // dismiss the password screen. | |
84 if (viewerPasswordScreen.active) | |
85 viewerPasswordScreen.accept(); | |
86 | |
87 viewerPageIndicator.initialFadeIn(); | |
88 viewerToolbar.initialFadeIn(); | |
89 break; | |
90 case 'loadProgress': | |
91 updateProgress(message.data.progress); | |
92 break; | |
93 case 'goToPage': | |
94 viewport.goToPage(message.data.page); | |
95 break; | |
96 case 'getPassword': | |
97 // If the password screen isn't up, put it up. Otherwise we're responding | |
98 // to an incorrect password so deny it. | |
99 if (!viewerPasswordScreen.active) | |
100 viewerPasswordScreen.active = true; | |
101 else | |
102 viewerPasswordScreen.deny(); | |
103 } | |
104 } | |
105 | |
106 // Callback that's called when the viewport changes. | |
107 function viewportChangedCallback(zoom, | |
108 x, | |
109 y, | |
110 scrollbarWidth, | |
111 hasScrollbars, | |
112 page) { | |
113 // Offset the toolbar position so that it doesn't move if scrollbars appear. | |
114 var toolbarRight = hasScrollbars.y ? 0 : scrollbarWidth; | |
115 var toolbarBottom = hasScrollbars.x ? 0 : scrollbarWidth; | |
116 viewerToolbar.style.right = toolbarRight + 'px'; | |
117 viewerToolbar.style.bottom = toolbarBottom + 'px'; | |
118 | |
119 // Show or hide the page indicator. | |
120 if (documentDimensions.pageDimensions.length > 1 && hasScrollbars.y) | |
121 viewerPageIndicator.style.visibility = 'visible'; | |
122 else | |
123 viewerPageIndicator.style.visibility = 'hidden'; | |
124 | |
125 // Update the most visible page. | |
126 viewerPageIndicator.text = page + 1; | |
127 | |
128 // Notify the plugin of the viewport change. | |
129 plugin.postMessage({ | |
130 type: 'viewport', | |
131 zoom: zoom, | |
132 xOffset: x, | |
133 yOffset: y | |
134 }); | |
135 } | |
136 | |
137 function load() { | |
138 sizer = $('sizer'); | |
139 viewerToolbar = $('toolbar'); | |
140 viewerPageIndicator = $('page-indicator'); | |
141 viewerProgressBar = $('progress-bar'); | |
142 viewerPasswordScreen = $('password-screen'); | |
143 viewerPasswordScreen.addEventListener('password-submitted', | |
144 onPasswordSubmitted); | |
145 viewerErrorScreen = $('error-screen'); | |
146 viewerErrorScreen.text = 'Failed to load PDF document'; | |
147 | 28 |
148 // Create the viewport. | 29 // Create the viewport. |
149 viewport = new Viewport(window, | 30 this.viewport_ = new Viewport(window, |
150 sizer, | 31 this.sizer_, |
151 isFitToPageEnabled, | 32 this.isFitToPageEnabled_.bind(this), |
152 viewportChangedCallback); | 33 this.viewportChangedCallback_.bind(this)); |
153 | 34 |
154 // Create the plugin object dynamically so we can set its src. | 35 // Create the plugin object dynamically so we can set its src. The plugin |
155 plugin = document.createElement('object'); | 36 // element is sized to fill the entire window and is set to be fixed |
156 plugin.id = 'plugin'; | 37 // positioning, acting as a viewport. The plugin renders into this viewport |
157 plugin.type = 'application/x-google-chrome-pdf'; | 38 // according to the scroll position of the window. |
158 plugin.addEventListener('message', handleMessage, false); | 39 this.plugin_ = document.createElement('object'); |
| 40 this.plugin_.id = 'plugin'; |
| 41 this.plugin_.type = 'application/x-google-chrome-pdf'; |
| 42 this.plugin_.addEventListener('message', this.handleMessage_.bind(this), |
| 43 false); |
159 // The pdf location is passed in stream details in the background page. | 44 // The pdf location is passed in stream details in the background page. |
160 var streamDetails = chrome.extension.getBackgroundPage().popStreamDetails(); | 45 var streamDetails = chrome.extension.getBackgroundPage().popStreamDetails(); |
161 plugin.setAttribute('src', streamDetails.streamUrl); | 46 this.plugin_.setAttribute('src', streamDetails.streamUrl); |
162 document.body.appendChild(plugin); | 47 document.body.appendChild(this.plugin_); |
163 | 48 |
164 // Setup the button event listeners. | 49 this.setupEventListeners_(streamDetails); |
165 $('fit-to-width-button').addEventListener('click', | 50 } |
166 viewport.fitToWidth.bind(viewport)); | 51 |
167 $('fit-to-page-button').addEventListener('click', | 52 PDFViewer.prototype = { |
168 viewport.fitToPage.bind(viewport)); | 53 /** |
169 $('zoom-in-button').addEventListener('click', | 54 * @private |
170 viewport.zoomIn.bind(viewport)); | 55 * Sets up event listeners for key shortcuts and also the UI buttons. |
171 $('zoom-out-button').addEventListener('click', | 56 * @param {Object} streamDetails the details of the original HTTP request for |
172 viewport.zoomOut.bind(viewport)); | 57 * the PDF. |
173 $('save-button-link').href = streamDetails.originalUrl; | 58 */ |
174 $('print-button').addEventListener('click', print); | 59 setupEventListeners_: function(streamDetails) { |
175 | 60 // Setup the button event listeners. |
176 | 61 $('fit-to-width-button').addEventListener('click', |
177 // Setup keyboard event listeners. | 62 this.viewport_.fitToWidth.bind(this.viewport_)); |
178 document.onkeydown = function(e) { | 63 $('fit-to-page-button').addEventListener('click', |
179 switch (e.keyCode) { | 64 this.viewport_.fitToPage.bind(this.viewport_)); |
180 case 37: // Left arrow key. | 65 $('zoom-in-button').addEventListener('click', |
181 // Go to the previous page if there are no horizontal scrollbars. | 66 this.viewport_.zoomIn.bind(this.viewport_)); |
182 if (!viewport.documentHasScrollbars().x) { | 67 $('zoom-out-button').addEventListener('click', |
183 viewport.goToPage(viewport.getMostVisiblePage() - 1); | 68 this.viewport_.zoomOut.bind(this.viewport_)); |
184 // Since we do the movement of the page. | 69 $('save-button-link').href = streamDetails.originalUrl; |
185 e.preventDefault(); | 70 $('print-button').addEventListener('click', this.print_.bind(this)); |
186 } | 71 |
187 return; | 72 // Setup keyboard event listeners. |
188 case 33: // Page up key. | 73 document.onkeydown = function(e) { |
189 // Go to the previous page if we are fit-to-page. | 74 switch (e.keyCode) { |
190 if (isFitToPageEnabled()) { | 75 case 37: // Left arrow key. |
191 viewport.goToPage(viewport.getMostVisiblePage() - 1); | 76 // Go to the previous page if there are no horizontal scrollbars. |
192 // Since we do the movement of the page. | 77 if (!this.viewport_.documentHasScrollbars().x) { |
193 e.preventDefault(); | 78 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1); |
194 } | 79 // Since we do the movement of the page. |
195 return; | 80 e.preventDefault(); |
196 case 39: // Right arrow key. | 81 } |
197 // Go to the next page if there are no horizontal scrollbars. | 82 return; |
198 if (!viewport.documentHasScrollbars().x) { | 83 case 33: // Page up key. |
199 viewport.goToPage(viewport.getMostVisiblePage() + 1); | 84 // Go to the previous page if we are fit-to-page. |
200 // Since we do the movement of the page. | 85 if (isFitToPageEnabled()) { |
201 e.preventDefault(); | 86 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1); |
202 } | 87 // Since we do the movement of the page. |
203 return; | 88 e.preventDefault(); |
204 case 34: // Page down key. | 89 } |
205 // Go to the next page if we are fit-to-page. | 90 return; |
206 if (isFitToPageEnabled()) { | 91 case 39: // Right arrow key. |
207 viewport.goToPage(viewport.getMostVisiblePage() + 1); | 92 // Go to the next page if there are no horizontal scrollbars. |
208 // Since we do the movement of the page. | 93 if (!this.viewport_.documentHasScrollbars().x) { |
209 e.preventDefault(); | 94 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1); |
210 } | 95 // Since we do the movement of the page. |
211 return; | 96 e.preventDefault(); |
212 case 187: // +/= key. | 97 } |
213 case 107: // Numpad + key. | 98 return; |
214 if (e.ctrlKey || e.metaKey) { | 99 case 34: // Page down key. |
215 viewport.zoomIn(); | 100 // Go to the next page if we are fit-to-page. |
216 // Since we do the zooming of the page. | 101 if (isFitToPageEnabled()) { |
217 e.preventDefault(); | 102 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1); |
218 } | 103 // Since we do the movement of the page. |
219 return; | 104 e.preventDefault(); |
220 case 189: // -/_ key. | 105 } |
221 case 109: // Numpad - key. | 106 return; |
222 if (e.ctrlKey || e.metaKey) { | 107 case 187: // +/= key. |
223 viewport.zoomOut(); | 108 case 107: // Numpad + key. |
224 // Since we do the zooming of the page. | 109 if (e.ctrlKey || e.metaKey) { |
225 e.preventDefault(); | 110 this.viewport_.zoomIn(); |
226 } | 111 // Since we do the zooming of the page. |
227 return; | 112 e.preventDefault(); |
228 case 83: // s key. | 113 } |
229 if (e.ctrlKey || e.metaKey) { | 114 return; |
230 // Simulate a click on the button so that the <a download ...> | 115 case 189: // -/_ key. |
231 // attribute is used. | 116 case 109: // Numpad - key. |
232 $('save-button-link').click(); | 117 if (e.ctrlKey || e.metaKey) { |
233 // Since we do the saving of the page. | 118 this.viewport_.zoomOut(); |
234 e.preventDefault(); | 119 // Since we do the zooming of the page. |
235 } | 120 e.preventDefault(); |
236 return; | 121 } |
237 case 80: // p key. | 122 return; |
238 if (e.ctrlKey || e.metaKey) { | 123 case 83: // s key. |
239 print(); | 124 if (e.ctrlKey || e.metaKey) { |
240 // Since we do the printing of the page. | 125 // Simulate a click on the button so that the <a download ...> |
241 e.preventDefault(); | 126 // attribute is used. |
242 } | 127 $('save-button-link').click(); |
243 return; | 128 // Since we do the saving of the page. |
| 129 e.preventDefault(); |
| 130 } |
| 131 return; |
| 132 case 80: // p key. |
| 133 if (e.ctrlKey || e.metaKey) { |
| 134 this.print_(); |
| 135 // Since we do the printing of the page. |
| 136 e.preventDefault(); |
| 137 } |
| 138 return; |
| 139 } |
| 140 }.bind(this); |
| 141 }, |
| 142 |
| 143 |
| 144 /** |
| 145 * @private |
| 146 * Notify the plugin to print. |
| 147 */ |
| 148 print_: function() { |
| 149 this.plugin_.postMessage({ |
| 150 type: 'print', |
| 151 }); |
| 152 }, |
| 153 |
| 154 /** |
| 155 * @private |
| 156 * @return {boolean} true if the fit-to-page button is enabled. |
| 157 */ |
| 158 isFitToPageEnabled_: function() { |
| 159 return $('fit-to-page-button').classList.contains('polymer-selected'); |
| 160 }, |
| 161 |
| 162 /** |
| 163 * @private |
| 164 * Update the loading progress of the document in response to a progress |
| 165 * message being received from the plugin. |
| 166 * @param {number} progress the progress as a percentage. |
| 167 */ |
| 168 updateProgress_: function(progress) { |
| 169 this.progressBar_.progress = progress; |
| 170 if (progress == -1) { |
| 171 // Document load failed. |
| 172 this.errorScreen_.style.visibility = 'visible'; |
| 173 this.sizer_.style.display = 'none'; |
| 174 this.toolbar_.style.visibility = 'hidden'; |
| 175 if (this.passwordScreen_.active) { |
| 176 this.passwordScreen_.deny(); |
| 177 this.passwordScreen_.active = false; |
| 178 } |
244 } | 179 } |
245 }; | 180 }, |
| 181 |
| 182 /** |
| 183 * @private |
| 184 * An event handler for handling password-submitted events. These are fired |
| 185 * when an event is entered into the password screen. |
| 186 * @param {Object} event a password-submitted event. |
| 187 */ |
| 188 onPasswordSubmitted_: function(event) { |
| 189 this.plugin_.postMessage({ |
| 190 type: 'getPasswordComplete', |
| 191 password: event.detail.password |
| 192 }); |
| 193 }, |
| 194 |
| 195 /** |
| 196 * @private |
| 197 * An event handler for handling message events received from the plugin. |
| 198 * @param {MessageObject} message a message event. |
| 199 */ |
| 200 handleMessage_: function(message) { |
| 201 switch (message.data.type.toString()) { |
| 202 case 'documentDimensions': |
| 203 this.documentDimensions_ = message.data; |
| 204 this.viewport_.setDocumentDimensions(this.documentDimensions_); |
| 205 this.toolbar_.style.visibility = 'visible'; |
| 206 // If we received the document dimensions, the password was good so we |
| 207 // can dismiss the password screen. |
| 208 if (this.passwordScreen_.active) |
| 209 this.passwordScreen_.accept(); |
| 210 |
| 211 this.pageIndicator_.initialFadeIn(); |
| 212 this.toolbar_.initialFadeIn(); |
| 213 break; |
| 214 case 'loadProgress': |
| 215 this.updateProgress_(message.data.progress); |
| 216 break; |
| 217 case 'goToPage': |
| 218 this.viewport_.goToPage(message.data.page); |
| 219 break; |
| 220 case 'getPassword': |
| 221 // If the password screen isn't up, put it up. Otherwise we're |
| 222 // responding to an incorrect password so deny it. |
| 223 if (!this.passwordScreen_.active) |
| 224 this.passwordScreen_.active = true; |
| 225 else |
| 226 this.passwordScreen_.deny(); |
| 227 } |
| 228 }, |
| 229 |
| 230 /** |
| 231 * @private |
| 232 * A callback that's called when the viewport changes. |
| 233 * @param {number} zoom the zoom level. |
| 234 * @param {number} x the x scroll coordinate. |
| 235 * @param {number} y the y scroll coordinate. |
| 236 * @param {number} scrollbarWidth the width of scrollbars on the page. |
| 237 * @param {Object} hasScrollbars whether the viewport has a |
| 238 * horizontal/vertical scrollbar. |
| 239 * @param {number} page the index of the most visible page in the viewport. |
| 240 */ |
| 241 viewportChangedCallback_: function(zoom, |
| 242 x, |
| 243 y, |
| 244 scrollbarWidth, |
| 245 hasScrollbars, |
| 246 page) { |
| 247 // Offset the toolbar position so that it doesn't move if scrollbars appear. |
| 248 var toolbarRight = hasScrollbars.y ? 0 : scrollbarWidth; |
| 249 var toolbarBottom = hasScrollbars.x ? 0 : scrollbarWidth; |
| 250 this.toolbar_.style.right = toolbarRight + 'px'; |
| 251 this.toolbar_.style.bottom = toolbarBottom + 'px'; |
| 252 |
| 253 // Show or hide the page indicator. |
| 254 if (this.documentDimensions_.pageDimensions.length > 1 && hasScrollbars.y) |
| 255 this.pageIndicator_.style.visibility = 'visible'; |
| 256 else |
| 257 this.pageIndicator_.style.visibility = 'hidden'; |
| 258 |
| 259 // Update the most visible page. |
| 260 this.pageIndicator_.text = page + 1; |
| 261 |
| 262 // Notify the plugin of the viewport change. |
| 263 this.plugin_.postMessage({ |
| 264 type: 'viewport', |
| 265 zoom: zoom, |
| 266 xOffset: x, |
| 267 yOffset: y |
| 268 }); |
| 269 }, |
246 } | 270 } |
247 | 271 |
248 load(); | 272 new PDFViewer(); |
249 | 273 |
250 })(); | 274 })(); |
OLD | NEW |