| 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 |