| 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..f524e51200ce0908f3503b73fa9d4bebbd2cad26
|
| --- /dev/null
|
| +++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
|
| @@ -0,0 +1,446 @@
|
| +// 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.
|
| +
|
| +// TODO Add mechanism to reshow the loading overlay if the plugin is too long to
|
| +// refresh.
|
| +
|
| +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.CustomMargins}
|
| + * @private
|
| + */
|
| + this.customMargins_ = new print_preview.CustomMargins(
|
| + this.printTicketStore_);
|
| + this.addChild(this.customMargins_);
|
| +
|
| + /**
|
| + * 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;
|
| +
|
| + /**
|
| + * A rectangle describing the postion of the most visible page normalized
|
| + * with respect to the total height and width of the plugin.
|
| + * @type {print_preview.Rect}
|
| + * @private
|
| + */
|
| + this.pageLocationNormalized_ = null;
|
| + };
|
| +
|
| + /**
|
| + * Enumeration of events dispatched by the preview area.
|
| + * @enum {string}
|
| + */
|
| + PreviewArea.Event = {
|
| + OPEN_SYSTEM_DIALOG_CLICK:
|
| + 'print_preview.PreviewArea.OPEN_SYSTEM_DIALOG_CLICK',
|
| + PREVIEW_GENERATION_FAIL: 'print_preview.PreviewArea.PREVIEW_GENERATION_FAIL'
|
| + };
|
| +
|
| + /**
|
| + * CSS classes used by the preview area.
|
| + * @enum {string}
|
| + * @private
|
| + */
|
| + PreviewArea.Classes_ = {
|
| + COMPATIBILITY_OBJECT: 'preview-area-compatibility-object',
|
| + LOADING_MESSAGE: 'preview-area-loading-message',
|
| + CUSTOM_MESSAGE: 'preview-area-custom-message',
|
| + CUSTOM_MESSAGE_TEXT: 'preview-area-custom-message-text',
|
| + 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',
|
| + PREVIEW_FAILED_MESSAGE: 'preview-failed-message'
|
| + };
|
| +
|
| + 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.
|
| + if (!this.plugin_) {
|
| + return;
|
| + }
|
| +
|
| + // We only care about: PageUp, PageDown, Left, Up, Right, Down.
|
| + if (!arrayContains([33, 34, 37, 38, 39, 40], e.keyCode)) {
|
| + return;
|
| + }
|
| +
|
| + // If the user is holding a modifier key, ignore.
|
| + if (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();
|
| + },
|
| +
|
| + showCustomMessage: function(message) {
|
| + var customMessageTextEl = this.getElement().getElementsByClassName(
|
| + PreviewArea.Classes_.CUSTOM_MESSAGE_TEXT)[0];
|
| + customMessageTextEl.textContent = message;
|
| + var customMessageEl = this.getElement().getElementsByClassName(
|
| + PreviewArea.Classes_.CUSTOM_MESSAGE)[0];
|
| + setIsVisible(customMessageEl, true);
|
| + },
|
| +
|
| + /** @override */
|
| + decorateInternal: function() {
|
| + this.customMargins_.decorate(this.getElement());
|
| + },
|
| +
|
| + /** @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.Event.INITIALIZE,
|
| + this.onTicketInitialize_.bind(this));
|
| + this.tracker.add(
|
| + this.printTicketStore_,
|
| + print_preview.PrintTicketStore.Event.TICKET_CHANGE,
|
| + this.onTicketChange_.bind(this));
|
| + this.tracker.add(
|
| + this.printTicketStore_,
|
| + print_preview.PrintTicketStore.Event.CAPABILITIES_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.Event.PAGE_READY,
|
| + this.onPagePreviewReady_.bind(this));
|
| + this.tracker.add(
|
| + this.previewGenerator_,
|
| + print_preview.PreviewGenerator.Event.FAIL,
|
| + this.onPreviewGenerationFail_.bind(this));
|
| + } else {
|
| + // Hide loading message and show no plugin message.
|
| + this.hideLoadingMessage_();
|
| + this.showCustomMessage(localStrings.getString('noPlugin'));
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * @return {Element} Preview overlay element.
|
| + * @private
|
| + */
|
| + get overlayEl_() {
|
| + return this.getElement().getElementsByClassName(
|
| + PreviewArea.Classes_.OVERLAY)[0];
|
| + },
|
| +
|
| + /**
|
| + * @return {HTMLButtonElement} Button used to open the system dialog in the
|
| + * event that no compatible preview plugin is found.
|
| + * @private
|
| + */
|
| + get openSystemDialogButton_() {
|
| + return 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;
|
| + },
|
| +
|
| + /**
|
| + * Hides the loading message.
|
| + * @private
|
| + */
|
| + hideLoadingMessage_: function() {
|
| + var loadingMessage = this.getElement().getElementsByClassName(
|
| + PreviewArea.Classes_.LOADING_MESSAGE)[0];
|
| + setIsVisible(loadingMessage, false);
|
| + },
|
| +
|
| + /**
|
| + * 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_) {
|
| + throw Error('Pdf preview plugin already created');
|
| + }
|
| + 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());
|
| + },
|
| +
|
| + /**
|
| + * 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.Event.OPEN_SYSTEM_DIALOG_CLICK);
|
| + },
|
| +
|
| + /**
|
| + * Called when the print ticket is initialized. Requests a preview from
|
| + * the preview generator.
|
| + * @private
|
| + */
|
| + onTicketInitialize_: function() {
|
| + if (this.previewGenerator_ && this.printTicketStore_.isTicketValid()) {
|
| + this.previewGenerator_.requestPreview();
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Called when the print ticket changes. Updates the preview.
|
| + * @private
|
| + */
|
| + onTicketChange_: function() {
|
| + if (this.previewGenerator_ && this.printTicketStore_.isTicketValid()) {
|
| + this.previewGenerator_.requestPreview();
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Called when a page preview has been generated. Updates the plugin with
|
| + * the new page.
|
| + * @param {cr.Event} evt Contains information about the page preview.
|
| + * @private
|
| + */
|
| + onPagePreviewReady_: function(evt) {
|
| + if (!this.plugin_) {
|
| + this.createPlugin_(evt.previewUrl);
|
| + }
|
| + if (evt.previewIndex == 0) {
|
| + this.plugin_.goToPage('0');
|
| + this.plugin_.resetPrintPreviewUrl(evt.previewUrl);
|
| + this.plugin_.reload();
|
| + this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
|
| + }
|
| + this.plugin_.loadPreviewPage(evt.previewUrl, evt.previewIndex);
|
| + },
|
| +
|
| + /**
|
| + * Called when the generation of a preview fails. Shows an error message.
|
| + * @private
|
| + */
|
| + onPreviewGenerationFail_: function() {
|
| + // TODO test this method.
|
| + this.overlayEl_.classList.remove('invisible');
|
| + var previewFailedMessage = this.getElement().getElementsByClassName(
|
| + PreviewArea.Classes_.PREVIEW_FAILED_MESSAGE)[0];
|
| + setIsVisible(previewFailedMessage, true);
|
| + cr.dispatchSimpleEvent(this, PreviewArea.Event.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() {
|
| + // Setting the plugin's page count can only be called after the plugin is
|
| + // loaded.
|
| + this.plugin_.printPreviewPageCount(
|
| + this.printTicketStore_.getPageNumberSet().size);
|
| + 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.overlayEl_.classList.add('invisible');
|
| + this.hideLoadingMessage_();
|
| + },
|
| +
|
| + /**
|
| + * 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() {
|
| + 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 - 9.0,
|
| + parseFloat(normalized[1]) * pluginHeight - 9.0);
|
| + this.customMargins_.updateTranslationTransform(translationTransform);
|
| + var pageWidthInPixels = parseFloat(normalized[2]) * pluginWidth;
|
| + this.customMargins_.updateScaleTransform(
|
| + pageWidthInPixels / this.printTicketStore_.pageSize.width);
|
| + }
|
| + };
|
| +
|
| + // Export
|
| + return {
|
| + PreviewArea: PreviewArea
|
| + };
|
| +});
|
|
|