Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(357)

Side by Side Diff: chrome/browser/resources/print_preview/previewarea/preview_area.js

Issue 10108001: Refactor print preview web ui (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 cr.define('print_preview', function() {
6 'use strict';
7
8 /**
9 * Creates a PreviewArea object. It represents the area where the preview
10 * document is displayed.
11 *
12 * @param {print_preview.PrintTicketStore!} printTicketStore Used to get
13 * information about how the preview should be displayed.
14 * @param {print_preview.PreviewGenerator!} previewGenerator Used to read
15 * generated page previews.
16 * @constructor
17 * @extends {print_preview.Component}
18 */
19 function PreviewArea(printTicketStore, previewGenerator) {
20 print_preview.Component.call(this);
21
22 /**
23 * Used to get information about how the preview should be displayed.
24 * @type {print_preview.PrintTicketStore!}
25 * @private
26 */
27 this.printTicketStore_ = printTicketStore;
28
29 /**
30 * Used to read generated page previews.
31 * @type {print_preview.PreviewGenerator?}
32 * @private
33 */
34 this.previewGenerator_ = null;
35
36 /**
37 * The embedded pdf plugin object. It's value is null if not yet loaded.
38 * @type {HTMLEmbedElement?}
39 * @private
40 */
41 this.plugin_ = null;
42
43 /**
44 * Current zoom level as a percentage.
45 * @type {number}
46 * @private
47 */
48 this.zoomLevel_ = null;
49
50 /**
51 * Current page offset which can be used to calculate scroll amount.
52 * @type {Object.<{x: number, y: number}>!}
53 * @private
54 */
55 this.pageOffset_ = {x: 0, y: 0};
56
57 /**
58 * A rectangle describing the postion of the most visible page normalized
59 * with respect to the total height and width of the plugin.
60 * @type {print_preview.Rect}
61 * @private
62 */
63 this.pageLocationNormalized_ = null;
64 };
65
66 /**
67 * Enumeration of events dispatched by the preview area.
68 * @enum {string}
69 */
70 PreviewArea.Event = {
71 OPEN_SYSTEM_DIALOG_CLICK:
72 'print_preview.PreviewArea.OPEN_SYSTEM_DIALOG_CLICK',
73 PREVIEW_GENERATION_FAIL: 'print_preview.PreviewArea.PREVIEW_GENERATION_FAIL'
74 };
75
76 /**
77 * CSS classes used by the preview area.
78 * @enum {string}
79 * @private
80 */
81 PreviewArea.Classes_ = {
82 COMPATIBILITY_OBJECT: 'preview-area-compatibility-object',
83 LOADING_MESSAGE: 'preview-area-loading-message',
84 NO_PLUGIN_MESSAGE: 'preview-area-no-plugin',
85 OPEN_SYSTEM_DIALOG_BUTTON: 'preview-area-open-system-dialog-button',
86 OPEN_SYSTEM_DIALOG_BUTTON_THROBBER:
87 'preview-area-open-system-dialog-button-throbber',
88 OVERLAY: 'preview-area-overlay-layer',
89 PDF_PLUGIN: 'preview-area-pdf-plugin',
90 PREVIEW_FAILED_MESSAGE: 'preview-failed-message'
91 };
92
93 PreviewArea.prototype = {
94 __proto__: print_preview.Component.prototype,
95
96 /**
97 * Should only be called after calling this.render().
98 * @return {boolean} Whether the preview area has a compatible plugin to
99 * display the print preview in.
100 */
101 get hasCompatiblePlugin() {
102 return this.previewGenerator_ != null;
103 },
104
105 handleDirectionalKeyEvent: function(e) {
106 // Make sure the PDF plugin is there.
107 if (!this.plugin_) {
108 return;
109 }
110
111 // We only care about: PageUp, PageDown, Left, Up, Right, Down.
112 if (!arrayContains([33, 34, 37, 38, 39, 40], e.keyCode)) {
113 return;
114 }
115
116 // If the user is holding a modifier key, ignore.
117 if (e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) {
118 return;
119 }
120
121 // Don't handle the key event for these elements.
122 var tagName = document.activeElement.tagName;
123 if (arrayContains(['INPUT', 'SELECT', 'EMBED'], tagName)) {
124 return;
125 }
126
127 // For the most part, if any div of header was the last clicked element,
128 // then the active element is the body. Starting with the last clicked
129 // element, and work up the DOM tree to see if any element has a
130 // scrollbar. If there exists a scrollbar, do not handle the key event
131 // here.
132 var element = document.activeElement;
133 if (element == document.body) {
134 if (lastClickedElement)
135 element = lastClickedElement;
136 while (element) {
137 if (element.scrollHeight > element.clientHeight)
138 return;
139 element = element.parentElement;
140 }
141 }
142
143 // No scroll bar anywhere, or the active element is something else, like a
144 // button. Note: buttons have a bigger scrollHeight than clientHeight.
145 this.plugin_.sendKeyEvent(e.keyCode);
146 e.preventDefault();
147 },
148
149 /** @override */
150 enterDocument: function() {
151 print_preview.Component.prototype.enterDocument.call(this);
152 this.tracker.add(
153 this.openSystemDialogButton_,
154 'click',
155 this.onOpenSystemDialogButtonClick_.bind(this));
156
157 this.tracker.add(
158 this.printTicketStore_,
159 print_preview.PrintTicketStore.Event.INITIALIZE,
160 this.onTicketInitialize_.bind(this));
161 this.tracker.add(
162 this.printTicketStore_,
163 print_preview.PrintTicketStore.Event.TICKET_CHANGE,
164 this.onTicketChange_.bind(this));
165 this.tracker.add(
166 this.printTicketStore_,
167 print_preview.PrintTicketStore.Event.CAPABILITIES_CHANGE,
168 this.onTicketChange_.bind(this));
169 this.tracker.add(
170 this.printTicketStore_,
171 print_preview.PrintTicketStore.Event.DOCUMENT_CHANGE,
172 this.onDocumentChange_.bind(this));
173
174 if (this.checkPluginCompatibility_()) {
175 this.previewGenerator_ = new print_preview.PreviewGenerator(
176 this.printTicketStore_);
177 this.tracker.add(
178 this.previewGenerator_,
179 print_preview.PreviewGenerator.Event.PAGE_READY,
180 this.onPagePreviewReady_.bind(this));
181 this.tracker.add(
182 this.previewGenerator_,
183 print_preview.PreviewGenerator.Event.DOCUMENT_READY,
184 this.onDocumentPreviewReady_.bind(this));
185 this.tracker.add(
186 this.previewGenerator_,
187 print_preview.PreviewGenerator.Event.FAIL,
188 this.onPreviewGenerationFail_.bind(this));
189 } else {
190 // Hide loading message and show no plugin message.
191 this.hideLoadingMessage_();
192 var noPluginMessage = this.getElement().getElementsByClassName(
193 PreviewArea.Classes_.NO_PLUGIN_MESSAGE)[0];
194 setIsVisible(noPluginMessage, true);;
195 }
196 },
197
198 get overlayEl_() {
199 return this.getElement().getElementsByClassName(
200 PreviewArea.Classes_.OVERLAY)[0];
201 },
202
203 get openSystemDialogButton_() {
204 return this.getElement().getElementsByClassName(
205 PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON)[0];
206 },
207
208 /**
209 * Checks to see if a suitable plugin for rendering the preview exists. If
210 * one does not exist, then an error message will be displayed.
211 * @return {boolean} Whether Chromium has a suitable plugin for rendering
212 * the preview.
213 * @private
214 */
215 checkPluginCompatibility_: function() {
216 var compatObj = this.getElement().getElementsByClassName(
217 PreviewArea.Classes_.COMPATIBILITY_OBJECT)[0];
218 var isCompatible =
219 compatObj.onload &&
220 compatObj.goToPage &&
221 compatObj.removePrintButton &&
222 compatObj.loadPreviewPage &&
223 compatObj.printPreviewPageCount &&
224 compatObj.resetPrintPreviewUrl &&
225 compatObj.onPluginSizeChanged &&
226 compatObj.onScroll &&
227 compatObj.pageXOffset &&
228 compatObj.pageYOffset &&
229 compatObj.setZoomLevel &&
230 compatObj.setPageNumbers &&
231 compatObj.setPageXOffset &&
232 compatObj.setPageYOffset &&
233 compatObj.getHorizontalScrollbarThickness &&
234 compatObj.getVerticalScrollbarThickness &&
235 compatObj.getPageLocationNormalized &&
236 compatObj.getHeight &&
237 compatObj.getWidth;
238 compatObj.parentElement.removeChild(compatObj);
239 return isCompatible;
240 },
241
242 /**
243 * Hides the loading message.
244 * @private
245 */
246 hideLoadingMessage_: function() {
247 var loadingMessage = this.getElement().getElementsByClassName(
248 PreviewArea.Classes_.LOADING_MESSAGE)[0];
249 setIsVisible(loadingMessage, false);
250 },
251
252 createPlugin_: function(srcUrl) {
253 if (this.plugin_) {
254 throw Error('Pdf preview plugin already created');
255 }
256 this.plugin_ = document.createElement('embed');
257 this.plugin_.setAttribute('class', 'preview-area-plugin');
258 this.plugin_.setAttribute(
259 'type', 'application/x-google-chrome-print-preview-pdf');
260 this.plugin_.setAttribute('src', srcUrl);
261 this.plugin_.setAttribute('aria-live', 'polite');
262 this.plugin_.setAttribute('aria-atomic', 'true');
263 this.getElement().appendChild(this.plugin_);
264
265 global['onPreviewPluginLoad'] = this.onPluginLoad_.bind(this);
266 this.plugin_.onload('onPreviewPluginLoad()');
267
268 global['onPreviewPluginVisualStateChange'] =
269 this.onPreviewVisualStateChange_.bind(this);
270 this.plugin_.onScroll('onPreviewPluginVisualStateChange()');
271 this.plugin_.onPluginSizeChanged('onPreviewPluginVisualStateChange()');
272
273 this.plugin_.removePrintButton();
274 this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
275
276 this.overlayEl_.classList.add('invisible');
277 this.hideLoadingMessage_();
278 },
279
280 /**
281 * Called when the open-system-dialog button is clicked. Disables the
282 * button, shows the throbber, and dispatches the OPEN_SYSTEM_DIALOG_CLICK
283 * event.
284 * @private
285 */
286 onOpenSystemDialogButtonClick_: function() {
287 this.openSystemDialogButton_.disabled = true;
288 var openSystemDialogThrobber = this.getElement().getElementsByClassName(
289 PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON_THROBBER)[0];
290 setIsVisible(openSystemDialogThrobber, true);
291 cr.dispatchSimpleEvent(this, PreviewArea.Event.OPEN_SYSTEM_DIALOG_CLICK);
292 },
293
294 onTicketInitialize_: function() {
295 if (this.previewGenerator_ && this.printTicketStore_.isTicketValid()) {
296 this.previewGenerator_.requestPreview();
297 }
298 },
299
300 /**
301 * Called when the print ticket changes. Updates the preview.
302 * @private
303 */
304 onTicketChange_: function() {
305 if (this.previewGenerator_ && this.printTicketStore_.isTicketValid()) {
306 this.previewGenerator_.requestPreview();
307 }
308 },
309
310 onDocumentChange_: function() {
311 if (this.plugin_) {
312 this.plugin_.printPreviewPageCount(
313 this.printTicketStore_.getPageNumberSet().size);
314 }
315 },
316
317 /**
318 * Called when a page preview has been generated. Updates the plugin with
319 * the new page.
320 * @param {cr.Event} evt Contains information about the page preview.
321 * @private
322 */
323 onPagePreviewReady_: function(evt) {
324 if (!this.plugin_) {
325 this.createPlugin_(evt.previewUrl);
326 }
327 if (evt.previewIndex == 0) {
328 this.plugin_.goToPage('0');
329 this.plugin_.resetPrintPreviewUrl(evt.previewUrl);
330 this.plugin_.reload();
331 this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
332 }
333 this.plugin_.loadPreviewPage(evt.previewUrl, evt.previewIndex);
334 // TODO May have to reposition the scroll.
335 },
336
337 onDocumentPreviewReady_: function() {
338 // TODO Delete me?
339 },
340
341 onPreviewGenerationFail_: function() {
342 this.overlayEl_.classList.remove('invisible');
343 var previewFailedMessage = this.getElement().getElementsByClassName(
344 PreviewArea.Classes_.PREVIEW_FAILED_MESSAGE)[0];
345 setIsVisible(previewFailedMessage, true);
346 cr.dispatchSimpleEvent(this, PreviewArea.Event.PREVIEW_GENERATION_FAIL);
347 },
348
349 onPluginLoad_: function() {
350 // Setting the plugin's page count can only be called after the plugin is
351 // loaded.
352 this.plugin_.printPreviewPageCount(
353 this.printTicketStore_.getPageNumberSet().size);
354 this.plugin_.fitToHeight();
355 // TODO
356 // if (this.zoomLevel_ != null && this.pageOffset_ != null) {
357 // this.plugin_.setZoomLevel(this.zoomLevel_);
358 // this.plugin_.setPageXOffset(this.pageOffset_.x);
359 // this.plugin_.setPageYOffset(this.pageOffset_.y);
360 // } else {
361 // this.plugin_.fitToHeight();
362 // }
363 },
364
365 onPreviewVisualStateChange_: function() {
366 log('print_preview.PreviewArea.onPreviewVisualStateChange_');
367 // TODO update margins UI since preview has moved.
368 },
369
370 /**
371 * The width of the plugin area in pixels, excluding any visible scrollbars,
372 * @type {number}
373 */
374 get width() {
375 return this.widthPercent * this.plugin_.offsetWidth;
376 },
377
378 /**
379 * The height of the plugin area in pixels, excluding any visible
380 * scrollbars.
381 * @type {number}
382 */
383 get height() {
384 return this.heightPercent * this.plugin_.offsetHeight;
385 },
386
387 /**
388 * The width of the plugin area in percent, excluding any visible
389 * scrollbars.
390 * @type {number}
391 */
392 get widthPercent() {
393 var width = this.plugin_.getWidth();
394 var scrollbarWidth = this.plugin_.getVerticalScrollbarThickness();
395 return (width - scrollbarWidth) / width;
396 },
397
398 /**
399 * The height of the plugin area in percent, excluding any visible
400 * scrollbars.
401 * @type {number}
402 */
403 get heightPercent() {
404 var height = this.plugin_.getHeight();
405 var scrollbarHeight = this.plugin_.getHorizontalScrollbarThickness();
406 return (height - scrollbarHeight) / height;
407 },
408
409 /**
410 * Queries the plugin for the location of the most visible page and updates
411 * |this.pageLocationNormalized|.
412 */
413 update: function() {
414 if (!this.pdfLoaded_)
415 return;
416 var pluginLocation =
417 this.plugin_.getPageLocationNormalized().split(';');
418 this.pageLocationNormalized = new print_preview.Rect(
419 parseFloat(pluginLocation[0]),
420 parseFloat(pluginLocation[1]),
421 parseFloat(pluginLocation[2]),
422 parseFloat(pluginLocation[3]));
423 },
424
425 /**
426 * Resets the state variables of |this|.
427 */
428 resetState: function() {
429 if (this.plugin_) {
430 this.zoomLevel_ = this.plugin_.getZoomLevel();
431 this.pageOffset_ = {
432 x: this.plugin_.pageXOffset(),
433 y: this.plugin_.pageYOffset()
434 };
435 }
436 this.pdfLoaded_ = false;
437 },
438
439 /**
440 * Hides the |this.overlayLayer| and any messages currently displayed.
441 */
442 hideOverlayLayer: function() {
443 this.tracker.add(this.overlayLayer, 'webkitTransitionEnd',
444 this.hideOverlayLayerCleanup_.bind(this), false);
445 if (this.plugin_)
446 this.plugin_.classList.remove('invisible');
447 this.overlayLayer.classList.add('invisible');
448 },
449
450 /**
451 * Necessary cleanup so that the dancing dots animation is not being
452 * rendered in the background when not displayed.
453 */
454 hideOverlayLayerCleanup_: function() {
455 this.customMessageWithDots_.hidden = true;
456 this.tracker.remove(this.overlayLayer, 'webkitTransitionEnd');
457 }
458 };
459
460 // Export
461 return {
462 PreviewArea: PreviewArea
463 };
464 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698