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

Unified 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: Resolve conflicts Created 8 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/print_preview/previewarea/preview_area.js
diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js
new file mode 100644
index 0000000000000000000000000000000000000000..1ede80a584b3192191f41ca0db5561c901e5b40c
--- /dev/null
+++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -0,0 +1,592 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('print_preview', function() {
+ 'use strict';
+
+ /**
+ * Creates a PreviewArea object. It represents the area where the preview
+ * document is displayed.
+ * @param {!print_preview.DestinationStore} destinationStore Used to get the
+ * currently selected destination.
+ * @param {!print_preview.PrintTicketStore} printTicketStore Used to get
+ * information about how the preview should be displayed.
+ * @param {!print_preview.NativeLayer} nativeLayer Needed to communicate with
+ * Chromium's preview generation system.
+ * @constructor
+ * @extends {print_preview.Component}
+ */
+ function PreviewArea(destinationStore, printTicketStore, nativeLayer) {
+ print_preview.Component.call(this);
+
+ /**
+ * Used to get the currently selected destination.
+ * @type {!print_preview.DestinationStore}
+ * @private
+ */
+ this.destinationStore_ = destinationStore;
+
+ /**
+ * Used to get information about how the preview should be displayed.
+ * @type {!print_preview.PrintTicketStore}
+ * @private
+ */
+ this.printTicketStore_ = printTicketStore;
+
+ /**
+ * Used to contruct the preview generator.
+ * @type {!print_preview.NativeLayer}
+ * @private
+ */
+ this.nativeLayer_ = nativeLayer;
+
+ /**
+ * Used to read generated page previews.
+ * @type {print_preview.PreviewGenerator}
+ * @private
+ */
+ this.previewGenerator_ = null;
+
+ /**
+ * The embedded pdf plugin object. It's value is null if not yet loaded.
+ * @type {HTMLEmbedElement}
+ * @private
+ */
+ this.plugin_ = null;
+
+ /**
+ * Custom margins component superimposed on the preview plugin.
+ * @type {!print_preview.MarginControlContainer}
+ * @private
+ */
+ this.marginControlContainer_ =
+ new print_preview.MarginControlContainer(this.printTicketStore_);
+ this.addChild(this.marginControlContainer_);
+
+ /**
+ * Current zoom level as a percentage.
+ * @type {?number}
+ * @private
+ */
+ this.zoomLevel_ = null;
+
+ /**
+ * Current page offset which can be used to calculate scroll amount.
+ * @type {print_preview.Coordinate2d}
+ * @private
+ */
+ this.pageOffset_ = null;
+
+ /**
+ * Whether the plugin has finished reloading.
+ * @type {boolean}
+ * @private
+ */
+ this.isPluginReloaded_ = false;
+
+ /**
+ * Whether the document preview is ready.
+ * @type {boolean}
+ * @private
+ */
+ this.isDocumentReady_ = false;
+
+ /**
+ * Timeout object used to display a loading message if the preview is taking
+ * a long time to generate.
+ * @type {Object}
+ * @private
+ */
+ this.loadingTimeout_ = null;
+
+ /**
+ * Overlay element.
+ * @type {HTMLElement}
+ * @private
+ */
+ this.overlayEl_ = null;
+
+ /**
+ * The "Open system dialog" button.
+ * @type {HTMLButtonElement}
+ * @private
+ */
+ this.openSystemDialogButton_ = null;
+ };
+
+ /**
+ * Event types dispatched by the preview area.
+ * @enum {string}
+ */
+ PreviewArea.EventType = {
+ // Dispatched when the "Open system dialog" button is clicked.
+ OPEN_SYSTEM_DIALOG_CLICK:
+ 'print_preview.PreviewArea.OPEN_SYSTEM_DIALOG_CLICK',
+
+ // Dispatched when the document preview is complete.
+ PREVIEW_GENERATION_DONE:
+ 'print_preview.PreviewArea.PREVIEW_GENERATION_DONE',
+
+ // Dispatched when the document preview failed to be generated.
+ PREVIEW_GENERATION_FAIL:
+ 'print_preview.PreviewArea.PREVIEW_GENERATION_FAIL',
+
+ // Dispatched when a new document preview is being generated.
+ PREVIEW_GENERATION_IN_PROGRESS:
+ 'print_preview.PreviewArea.PREVIEW_GENERATION_IN_PROGRESS'
+ };
+
+ /**
+ * CSS classes used by the preview area.
+ * @enum {string}
+ * @private
+ */
+ PreviewArea.Classes_ = {
+ COMPATIBILITY_OBJECT: 'preview-area-compatibility-object',
+ CUSTOM_MESSAGE_TEXT: 'preview-area-custom-message-text',
+ MESSAGE: 'preview-area-message',
+ INVISIBLE: 'invisible',
+ OPEN_SYSTEM_DIALOG_BUTTON: 'preview-area-open-system-dialog-button',
+ OPEN_SYSTEM_DIALOG_BUTTON_THROBBER:
+ 'preview-area-open-system-dialog-button-throbber',
+ OVERLAY: 'preview-area-overlay-layer',
+ PDF_PLUGIN: 'preview-area-pdf-plugin'
+ };
+
+ /**
+ * Enumeration of IDs shown in the preview area.
+ * @enum {string}
+ * @private
+ */
+ PreviewArea.MessageId_ = {
+ CUSTOM: 'custom',
+ LOADING: 'loading',
+ PREVIEW_FAILED: 'preview-failed'
+ };
+
+ /**
+ * Maps message IDs to the CSS class that contains them.
+ * @type {object.<PreviewArea.MessageId_, string>}
+ * @private
+ */
+ PreviewArea.MessageIdClassMap_ = {};
+ PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.CUSTOM] =
+ 'preview-area-custom-message';
+ PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.LOADING] =
+ 'preview-area-loading-message';
+ PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.PREVIEW_FAILED] =
+ 'preview-area-preview-failed-message';
+
+ /**
+ * Amount of time in milliseconds to wait after issueing a new preview before
+ * the loading message is shown.
+ * @type {number}
+ * @const
+ * @private
+ */
+ PreviewArea.LOADING_TIMEOUT_ = 200;
+
+ PreviewArea.prototype = {
+ __proto__: print_preview.Component.prototype,
+
+ /**
+ * Should only be called after calling this.render().
+ * @return {boolean} Whether the preview area has a compatible plugin to
+ * display the print preview in.
+ */
+ get hasCompatiblePlugin() {
+ return this.previewGenerator_ != null;
+ },
+
+ /**
+ * Processes a keyboard event that could possibly be used to change state of
+ * the preview plugin.
+ * @param {MouseEvent} e Mouse event to process.
+ */
+ handleDirectionalKeyEvent: function(e) {
+ // Make sure the PDF plugin is there.
+ // We only care about: PageUp, PageDown, Left, Up, Right, Down.
+ // If the user is holding a modifier key, ignore.
+ if (!this.plugin_ ||
+ !arrayContains([33, 34, 37, 38, 39, 40], e.keyCode) ||
+ e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) {
+ return;
+ }
+
+ // Don't handle the key event for these elements.
+ var tagName = document.activeElement.tagName;
+ if (arrayContains(['INPUT', 'SELECT', 'EMBED'], tagName)) {
+ return;
+ }
+
+ // For the most part, if any div of header was the last clicked element,
+ // then the active element is the body. Starting with the last clicked
+ // element, and work up the DOM tree to see if any element has a
+ // scrollbar. If there exists a scrollbar, do not handle the key event
+ // here.
+ var element = e.target;
+ while (element) {
+ if (element.scrollHeight > element.clientHeight ||
+ element.scrollWidth > element.clientWidth) {
+ return;
+ }
+ element = element.parentElement;
+ }
+
+ // No scroll bar anywhere, or the active element is something else, like a
+ // button. Note: buttons have a bigger scrollHeight than clientHeight.
+ this.plugin_.sendKeyEvent(e.keyCode);
+ e.preventDefault();
+ },
+
+ /**
+ * Shows a custom message on the preview area's overlay.
+ * @param {string} message Custom message to show.
+ */
+ showCustomMessage: function(message) {
+ this.showMessage_(PreviewArea.MessageId_.CUSTOM, message);
+ },
+
+ /** @override */
+ enterDocument: function() {
+ print_preview.Component.prototype.enterDocument.call(this);
+ this.tracker.add(
+ this.openSystemDialogButton_,
+ 'click',
+ this.onOpenSystemDialogButtonClick_.bind(this));
+
+ this.tracker.add(
+ this.printTicketStore_,
+ print_preview.PrintTicketStore.EventType.INITIALIZE,
+ this.onTicketChange_.bind(this));
+ this.tracker.add(
+ this.printTicketStore_,
+ print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
+ this.onTicketChange_.bind(this));
+ this.tracker.add(
+ this.printTicketStore_,
+ print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
+ this.onTicketChange_.bind(this));
+ this.tracker.add(
+ this.printTicketStore_,
+ print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
+ this.onTicketChange_.bind(this));
+
+ if (this.checkPluginCompatibility_()) {
+ this.previewGenerator_ = new print_preview.PreviewGenerator(
+ this.destinationStore_, this.printTicketStore_, this.nativeLayer_);
+ this.tracker.add(
+ this.previewGenerator_,
+ print_preview.PreviewGenerator.EventType.PREVIEW_START,
+ this.onPreviewStart_.bind(this));
+ this.tracker.add(
+ this.previewGenerator_,
+ print_preview.PreviewGenerator.EventType.PAGE_READY,
+ this.onPagePreviewReady_.bind(this));
+ this.tracker.add(
+ this.previewGenerator_,
+ print_preview.PreviewGenerator.EventType.FAIL,
+ this.onPreviewGenerationFail_.bind(this));
+ this.tracker.add(
+ this.previewGenerator_,
+ print_preview.PreviewGenerator.EventType.DOCUMENT_READY,
+ this.onDocumentReady_.bind(this));
+ } else {
+ this.showCustomMessage(localStrings.getString('noPlugin'));
+ }
+ },
+
+ /** @override */
+ exitDocument: function() {
+ print_preview.Component.prototype.exitDocument.call(this);
+ if (this.previewGenerator_) {
+ this.previewGenerator_.removeEventListeners();
+ }
+ this.overlayEl_ = null;
+ this.openSystemDialogButton_ = null;
+ },
+
+ /** @override */
+ decorateInternal: function() {
+ this.marginControlContainer_.decorate(this.getElement());
+ this.overlayEl_ = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.OVERLAY)[0];
+ this.openSystemDialogButton_ = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON)[0];
+ },
+
+ /**
+ * Checks to see if a suitable plugin for rendering the preview exists. If
+ * one does not exist, then an error message will be displayed.
+ * @return {boolean} Whether Chromium has a suitable plugin for rendering
+ * the preview.
+ * @private
+ */
+ checkPluginCompatibility_: function() {
+ var compatObj = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.COMPATIBILITY_OBJECT)[0];
+ var isCompatible =
+ compatObj.onload &&
+ compatObj.goToPage &&
+ compatObj.removePrintButton &&
+ compatObj.loadPreviewPage &&
+ compatObj.printPreviewPageCount &&
+ compatObj.resetPrintPreviewUrl &&
+ compatObj.onPluginSizeChanged &&
+ compatObj.onScroll &&
+ compatObj.pageXOffset &&
+ compatObj.pageYOffset &&
+ compatObj.setZoomLevel &&
+ compatObj.setPageNumbers &&
+ compatObj.setPageXOffset &&
+ compatObj.setPageYOffset &&
+ compatObj.getHorizontalScrollbarThickness &&
+ compatObj.getVerticalScrollbarThickness &&
+ compatObj.getPageLocationNormalized &&
+ compatObj.getHeight &&
+ compatObj.getWidth;
+ compatObj.parentElement.removeChild(compatObj);
+ return isCompatible;
+ },
+
+ /**
+ * Shows a given message on the overlay.
+ * @param {print_preview.PreviewArea.MessageId_} messageId ID of the message
+ * to show.
+ * @param {string=} opt_message Optional message to show that can be used
+ * by some message IDs.
+ * @private
+ */
+ showMessage_: function(messageId, opt_message) {
+ // Hide all messages.
+ var messageEls = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.MESSAGE);
+ for (var i = 0, messageEl; messageEl = messageEls[i]; i++) {
+ setIsVisible(messageEl, false);
+ }
+ // Disable jumping animation to conserve cycles.
+ var jumpingDotsEl = this.getElement().querySelector(
+ '.preview-area-loading-message-jumping-dots');
+ jumpingDotsEl.classList.remove('jumping-dots');
+
+ // Show specific message.
+ if (messageId == PreviewArea.MessageId_.CUSTOM) {
+ var customMessageTextEl = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.CUSTOM_MESSAGE_TEXT)[0];
+ customMessageTextEl.textContent = opt_message;
+ } else if (messageId == PreviewArea.MessageId_.LOADING) {
+ jumpingDotsEl.classList.add('jumping-dots');
+ }
+ var messageEl = this.getElement().getElementsByClassName(
+ PreviewArea.MessageIdClassMap_[messageId])[0];
+ setIsVisible(messageEl, true);
+
+ // Show overlay.
+ this.overlayEl_.classList.remove(PreviewArea.Classes_.INVISIBLE);
+ },
+
+ /**
+ * Hides the message overlay.
+ * @private
+ */
+ hideOverlay_: function() {
+ this.overlayEl_.classList.add(PreviewArea.Classes_.INVISIBLE);
+ // Disable jumping animation to conserve cycles.
+ var jumpingDotsEl = this.getElement().querySelector(
+ '.preview-area-loading-message-jumping-dots');
+ jumpingDotsEl.classList.remove('jumping-dots');
+ },
+
+ /**
+ * Creates a preview plugin and adds it to the DOM.
+ * @param {string} srcUrl Initial URL of the plugin.
+ * @private
+ */
+ createPlugin_: function(srcUrl) {
+ if (this.plugin_) {
+ console.warn('Pdf preview plugin already created');
+ return;
+ }
+ this.plugin_ = document.createElement('embed');
+ // NOTE: The plugin's 'id' field must be set to 'pdf-viewer' since
+ // chrome/renderer/print_web_view_helper.cc actually references it.
+ this.plugin_.setAttribute('id', 'pdf-viewer');
+ this.plugin_.setAttribute('class', 'preview-area-plugin');
+ this.plugin_.setAttribute(
+ 'type', 'application/x-google-chrome-print-preview-pdf');
+ this.plugin_.setAttribute('src', srcUrl);
+ this.plugin_.setAttribute('aria-live', 'polite');
+ this.plugin_.setAttribute('aria-atomic', 'true');
+ this.getElement().appendChild(this.plugin_);
+
+ global['onPreviewPluginLoad'] = this.onPluginLoad_.bind(this);
+ this.plugin_.onload('onPreviewPluginLoad()');
+
+ global['onPreviewPluginVisualStateChange'] =
+ this.onPreviewVisualStateChange_.bind(this);
+ this.plugin_.onScroll('onPreviewPluginVisualStateChange()');
+ this.plugin_.onPluginSizeChanged('onPreviewPluginVisualStateChange()');
+
+ this.plugin_.removePrintButton();
+ this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
+ },
+
+ /**
+ * Dispatches a PREVIEW_GENERATION_DONE event if all conditions are met.
+ * @private
+ */
+ dispatchPreviewGenerationDoneIfReady_: function() {
+ if (this.isDocumentReady_ && this.isPluginReloaded_) {
+ cr.dispatchSimpleEvent(
+ this, PreviewArea.EventType.PREVIEW_GENERATION_DONE);
+ this.marginControlContainer_.showMarginControlsIfNeeded();
+ }
+ },
+
+ /**
+ * Called when the open-system-dialog button is clicked. Disables the
+ * button, shows the throbber, and dispatches the OPEN_SYSTEM_DIALOG_CLICK
+ * event.
+ * @private
+ */
+ onOpenSystemDialogButtonClick_: function() {
+ this.openSystemDialogButton_.disabled = true;
+ var openSystemDialogThrobber = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON_THROBBER)[0];
+ setIsVisible(openSystemDialogThrobber, true);
+ cr.dispatchSimpleEvent(
+ this, PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK);
+ },
+
+ /**
+ * Called when the print ticket changes. Updates the preview.
+ * @private
+ */
+ onTicketChange_: function() {
+ if (this.previewGenerator_ && this.previewGenerator_.requestPreview()) {
+ if (this.loadingTimeout_ == null) {
+ this.loadingTimeout_ = setTimeout(
+ this.showMessage_.bind(this, PreviewArea.MessageId_.LOADING),
+ PreviewArea.LOADING_TIMEOUT_);
+ }
+ } else {
+ this.marginControlContainer_.showMarginControlsIfNeeded();
+ }
+ },
+
+ /**
+ * Called when the preview generator begins loading the preview.
+ * @param {cr.Event} Contains the URL to initialize the plugin to.
+ * @private
+ */
+ onPreviewStart_: function(event) {
+ this.isDocumentReady_ = false;
+ this.isPluginReloaded_ = false;
+ if (!this.plugin_) {
+ this.createPlugin_(event.previewUrl);
+ }
+ this.plugin_.goToPage('0');
+ this.plugin_.resetPrintPreviewUrl(event.previewUrl);
+ this.plugin_.reload();
+ this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
+ cr.dispatchSimpleEvent(
+ this, PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS);
+ },
+
+ /**
+ * Called when a page preview has been generated. Updates the plugin with
+ * the new page.
+ * @param {cr.Event} event Contains information about the page preview.
+ * @private
+ */
+ onPagePreviewReady_: function(event) {
+ this.plugin_.loadPreviewPage(event.previewUrl, event.previewIndex);
+ },
+
+ /**
+ * Called when the preview generation is complete and the document is ready
+ * to print.
+ * @private
+ */
+ onDocumentReady_: function(event) {
+ this.isDocumentReady_ = true;
+ this.dispatchPreviewGenerationDoneIfReady_();
+ },
+
+ /**
+ * Called when the generation of a preview fails. Shows an error message.
+ * @private
+ */
+ onPreviewGenerationFail_: function() {
+ this.showMessage_(PreviewArea.MessageId_.PREVIEW_FAILED);
+ cr.dispatchSimpleEvent(
+ this, PreviewArea.EventType.PREVIEW_GENERATION_FAIL);
+ },
+
+ /**
+ * Called when the plugin loads. This is a consequence of calling
+ * plugin.reload(). Certain plugin state can only be set after the plugin
+ * has loaded.
+ * @private
+ */
+ onPluginLoad_: function() {
+ if (this.loadingTimeout_) {
+ clearTimeout(this.loadingTimeout_);
+ this.loadingTimeout_ = null;
+ }
+ // Setting the plugin's page count can only be called after the plugin is
+ // loaded and the document must be modifiable.
+ if (this.printTicketStore_.isDocumentModifiable) {
+ this.plugin_.printPreviewPageCount(
+ this.printTicketStore_.getPageNumberSet().size);
+ }
+ this.plugin_.setPageNumbers(JSON.stringify(
+ this.printTicketStore_.getPageNumberSet().asArray()));
+ if (this.zoomLevel_ != null && this.pageOffset_ != null) {
+ this.plugin_.setZoomLevel(this.zoomLevel_);
+ this.plugin_.setPageXOffset(this.pageOffset_.x);
+ this.plugin_.setPageYOffset(this.pageOffset_.y);
+ } else {
+ this.plugin_.fitToHeight();
+ }
+ this.hideOverlay_();
+ this.isPluginReloaded_ = true;
+ this.dispatchPreviewGenerationDoneIfReady_();
+ },
+
+ /**
+ * Called when the preview plugin's visual state has changed. This is a
+ * consequence of scrolling or zooming the plugin. Updates the custom
+ * margins component if shown.
+ * @private
+ */
+ onPreviewVisualStateChange_: function() {
+ if (this.isPluginReloaded_) {
+ this.zoomLevel_ = this.plugin_.getZoomLevel();
+ this.pageOffset_ = new print_preview.Coordinate2d(
+ this.plugin_.pageXOffset(), this.plugin_.pageYOffset());
+ }
+ var normalized = this.plugin_.getPageLocationNormalized().split(';');
+ var pluginWidth = this.plugin_.getWidth();
+ var pluginHeight = this.plugin_.getHeight();
+ var translationTransform = new print_preview.Coordinate2d(
+ parseFloat(normalized[0]) * pluginWidth,
+ parseFloat(normalized[1]) * pluginHeight);
+ this.marginControlContainer_.updateTranslationTransform(
+ translationTransform);
+ var pageWidthInPixels = parseFloat(normalized[2]) * pluginWidth;
+ this.marginControlContainer_.updateScaleTransform(
+ pageWidthInPixels / this.printTicketStore_.pageSize.width);
+ this.marginControlContainer_.updateClippingMask(
+ new print_preview.Size(
+ pluginWidth - this.plugin_.getVerticalScrollbarThickness(),
+ pluginHeight - this.plugin_.getHorizontalScrollbarThickness()));
+ }
+ };
+
+ // Export
+ return {
+ PreviewArea: PreviewArea
+ };
+});

Powered by Google App Engine
This is Rietveld 408576698