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..4e76ee2a8a086c326d2baddb1abf42ab83065938 |
--- /dev/null |
+++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js |
@@ -0,0 +1,464 @@ |
+// 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.PrintTicketStore!} printTicketStore Used to get |
+ * information about how the preview should be displayed. |
+ * @param {print_preview.PreviewGenerator!} previewGenerator Used to read |
+ * generated page previews. |
+ * @constructor |
+ * @extends {print_preview.Component} |
+ */ |
+ function PreviewArea(printTicketStore, previewGenerator) { |
+ print_preview.Component.call(this); |
+ |
+ /** |
+ * Used to get information about how the preview should be displayed. |
+ * @type {print_preview.PrintTicketStore!} |
+ * @private |
+ */ |
+ this.printTicketStore_ = printTicketStore; |
+ |
+ /** |
+ * 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; |
+ |
+ /** |
+ * Current zoom level as a percentage. |
+ * @type {number} |
+ * @private |
+ */ |
+ this.zoomLevel_ = null; |
+ |
+ /** |
+ * Current page offset which can be used to calculate scroll amount. |
+ * @type {Object.<{x: number, y: number}>!} |
+ * @private |
+ */ |
+ this.pageOffset_ = {x: 0, y: 0}; |
+ |
+ /** |
+ * 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', |
+ NO_PLUGIN_MESSAGE: 'preview-area-no-plugin', |
+ 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; |
+ }, |
+ |
+ 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 = document.activeElement; |
+ if (element == document.body) { |
+ if (lastClickedElement) |
+ element = lastClickedElement; |
+ while (element) { |
+ if (element.scrollHeight > element.clientHeight) |
+ 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(); |
+ }, |
+ |
+ /** @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)); |
+ this.tracker.add( |
+ this.printTicketStore_, |
+ print_preview.PrintTicketStore.Event.DOCUMENT_CHANGE, |
+ this.onDocumentChange_.bind(this)); |
+ |
+ if (this.checkPluginCompatibility_()) { |
+ this.previewGenerator_ = new print_preview.PreviewGenerator( |
+ this.printTicketStore_); |
+ this.tracker.add( |
+ this.previewGenerator_, |
+ print_preview.PreviewGenerator.Event.PAGE_READY, |
+ this.onPagePreviewReady_.bind(this)); |
+ this.tracker.add( |
+ this.previewGenerator_, |
+ print_preview.PreviewGenerator.Event.DOCUMENT_READY, |
+ this.onDocumentPreviewReady_.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_(); |
+ var noPluginMessage = this.getElement().getElementsByClassName( |
+ PreviewArea.Classes_.NO_PLUGIN_MESSAGE)[0]; |
+ setIsVisible(noPluginMessage, true);; |
+ } |
+ }, |
+ |
+ get overlayEl_() { |
+ return this.getElement().getElementsByClassName( |
+ PreviewArea.Classes_.OVERLAY)[0]; |
+ }, |
+ |
+ 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); |
+ }, |
+ |
+ createPlugin_: function(srcUrl) { |
+ if (this.plugin_) { |
+ throw Error('Pdf preview plugin already created'); |
+ } |
+ this.plugin_ = document.createElement('embed'); |
+ 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()); |
+ |
+ this.overlayEl_.classList.add('invisible'); |
+ this.hideLoadingMessage_(); |
+ }, |
+ |
+ /** |
+ * 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); |
+ }, |
+ |
+ 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(); |
+ } |
+ }, |
+ |
+ onDocumentChange_: function() { |
+ if (this.plugin_) { |
+ this.plugin_.printPreviewPageCount( |
+ this.printTicketStore_.getPageNumberSet().size); |
+ } |
+ }, |
+ |
+ /** |
+ * 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); |
+ // TODO May have to reposition the scroll. |
+ }, |
+ |
+ onDocumentPreviewReady_: function() { |
+ // TODO Delete me? |
+ }, |
+ |
+ onPreviewGenerationFail_: function() { |
+ 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); |
+ }, |
+ |
+ onPluginLoad_: function() { |
+ // Setting the plugin's page count can only be called after the plugin is |
+ // loaded. |
+ this.plugin_.printPreviewPageCount( |
+ this.printTicketStore_.getPageNumberSet().size); |
+ this.plugin_.fitToHeight(); |
+ // TODO |
+// 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(); |
+// } |
+ }, |
+ |
+ onPreviewVisualStateChange_: function() { |
+ log('print_preview.PreviewArea.onPreviewVisualStateChange_'); |
+ // TODO update margins UI since preview has moved. |
+ }, |
+ |
+ /** |
+ * The width of the plugin area in pixels, excluding any visible scrollbars, |
+ * @type {number} |
+ */ |
+ get width() { |
+ return this.widthPercent * this.plugin_.offsetWidth; |
+ }, |
+ |
+ /** |
+ * The height of the plugin area in pixels, excluding any visible |
+ * scrollbars. |
+ * @type {number} |
+ */ |
+ get height() { |
+ return this.heightPercent * this.plugin_.offsetHeight; |
+ }, |
+ |
+ /** |
+ * The width of the plugin area in percent, excluding any visible |
+ * scrollbars. |
+ * @type {number} |
+ */ |
+ get widthPercent() { |
+ var width = this.plugin_.getWidth(); |
+ var scrollbarWidth = this.plugin_.getVerticalScrollbarThickness(); |
+ return (width - scrollbarWidth) / width; |
+ }, |
+ |
+ /** |
+ * The height of the plugin area in percent, excluding any visible |
+ * scrollbars. |
+ * @type {number} |
+ */ |
+ get heightPercent() { |
+ var height = this.plugin_.getHeight(); |
+ var scrollbarHeight = this.plugin_.getHorizontalScrollbarThickness(); |
+ return (height - scrollbarHeight) / height; |
+ }, |
+ |
+ /** |
+ * Queries the plugin for the location of the most visible page and updates |
+ * |this.pageLocationNormalized|. |
+ */ |
+ update: function() { |
+ if (!this.pdfLoaded_) |
+ return; |
+ var pluginLocation = |
+ this.plugin_.getPageLocationNormalized().split(';'); |
+ this.pageLocationNormalized = new print_preview.Rect( |
+ parseFloat(pluginLocation[0]), |
+ parseFloat(pluginLocation[1]), |
+ parseFloat(pluginLocation[2]), |
+ parseFloat(pluginLocation[3])); |
+ }, |
+ |
+ /** |
+ * Resets the state variables of |this|. |
+ */ |
+ resetState: function() { |
+ if (this.plugin_) { |
+ this.zoomLevel_ = this.plugin_.getZoomLevel(); |
+ this.pageOffset_ = { |
+ x: this.plugin_.pageXOffset(), |
+ y: this.plugin_.pageYOffset() |
+ }; |
+ } |
+ this.pdfLoaded_ = false; |
+ }, |
+ |
+ /** |
+ * Hides the |this.overlayLayer| and any messages currently displayed. |
+ */ |
+ hideOverlayLayer: function() { |
+ this.tracker.add(this.overlayLayer, 'webkitTransitionEnd', |
+ this.hideOverlayLayerCleanup_.bind(this), false); |
+ if (this.plugin_) |
+ this.plugin_.classList.remove('invisible'); |
+ this.overlayLayer.classList.add('invisible'); |
+ }, |
+ |
+ /** |
+ * Necessary cleanup so that the dancing dots animation is not being |
+ * rendered in the background when not displayed. |
+ */ |
+ hideOverlayLayerCleanup_: function() { |
+ this.customMessageWithDots_.hidden = true; |
+ this.tracker.remove(this.overlayLayer, 'webkitTransitionEnd'); |
+ } |
+ }; |
+ |
+ // Export |
+ return { |
+ PreviewArea: PreviewArea |
+ }; |
+}); |