Home | History | Annotate | Download | only in print_preview
      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 // TODO(rltoscano): Move data/* into print_preview.data namespace
      6 
      7 var localStrings = new LocalStrings(templateData);
      8 
      9 <include src="component.js"/>
     10 
     11 cr.define('print_preview', function() {
     12   'use strict';
     13 
     14   /**
     15    * Container class for Chromium's print preview.
     16    * @constructor
     17    * @extends {print_preview.Component}
     18    */
     19   function PrintPreview() {
     20     print_preview.Component.call(this);
     21 
     22     /**
     23      * Used to communicate with Chromium's print system.
     24      * @type {!print_preview.NativeLayer}
     25      * @private
     26      */
     27     this.nativeLayer_ = new print_preview.NativeLayer();
     28 
     29     /**
     30      * Event target that contains information about the logged in user.
     31      * @type {!print_preview.UserInfo}
     32      * @private
     33      */
     34     this.userInfo_ = new print_preview.UserInfo();
     35 
     36     /**
     37      * Metrics object used to report usage statistics.
     38      * @type {!print_preview.Metrics}
     39      * @private
     40      */
     41     this.metrics_ = new print_preview.Metrics();
     42 
     43     /**
     44      * Application state.
     45      * @type {!print_preview.AppState}
     46      * @private
     47      */
     48     this.appState_ = new print_preview.AppState();
     49 
     50     /**
     51      * Data model that holds information about the document to print.
     52      * @type {!print_preview.DocumentInfo}
     53      * @private
     54      */
     55     this.documentInfo_ = new print_preview.DocumentInfo();
     56 
     57     /**
     58      * Data store which holds print destinations.
     59      * @type {!print_preview.DestinationStore}
     60      * @private
     61      */
     62     this.destinationStore_ = new print_preview.DestinationStore(
     63         this.nativeLayer_, this.appState_, this.metrics_);
     64 
     65     /**
     66      * Storage of the print ticket used to create the print job.
     67      * @type {!print_preview.PrintTicketStore}
     68      * @private
     69      */
     70     this.printTicketStore_ = new print_preview.PrintTicketStore(
     71         this.destinationStore_, this.appState_, this.documentInfo_);
     72 
     73     /**
     74      * Holds the print and cancel buttons and renders some document statistics.
     75      * @type {!print_preview.PrintHeader}
     76      * @private
     77      */
     78     this.printHeader_ = new print_preview.PrintHeader(
     79         this.printTicketStore_, this.destinationStore_);
     80     this.addChild(this.printHeader_);
     81 
     82     /**
     83      * Component used to search for print destinations.
     84      * @type {!print_preview.DestinationSearch}
     85      * @private
     86      */
     87     this.destinationSearch_ = new print_preview.DestinationSearch(
     88         this.destinationStore_, this.userInfo_, this.metrics_);
     89     this.addChild(this.destinationSearch_);
     90 
     91     /**
     92      * Component that renders the print destination.
     93      * @type {!print_preview.DestinationSettings}
     94      * @private
     95      */
     96     this.destinationSettings_ = new print_preview.DestinationSettings(
     97         this.destinationStore_);
     98     this.addChild(this.destinationSettings_);
     99 
    100     /**
    101      * Component that renders UI for entering in page range.
    102      * @type {!print_preview.PageSettings}
    103      * @private
    104      */
    105     this.pageSettings_ = new print_preview.PageSettings(
    106         this.printTicketStore_.pageRange);
    107     this.addChild(this.pageSettings_);
    108 
    109     /**
    110      * Component that renders the copies settings.
    111      * @type {!print_preview.CopiesSettings}
    112      * @private
    113      */
    114     this.copiesSettings_ = new print_preview.CopiesSettings(
    115         this.printTicketStore_.copies, this.printTicketStore_.collate);
    116     this.addChild(this.copiesSettings_);
    117 
    118     /**
    119      * Component that renders the layout settings.
    120      * @type {!print_preview.LayoutSettings}
    121      * @private
    122      */
    123     this.layoutSettings_ =
    124         new print_preview.LayoutSettings(this.printTicketStore_.landscape);
    125     this.addChild(this.layoutSettings_);
    126 
    127     /**
    128      * Component that renders the color options.
    129      * @type {!print_preview.ColorSettings}
    130      * @private
    131      */
    132     this.colorSettings_ =
    133         new print_preview.ColorSettings(this.printTicketStore_.color);
    134     this.addChild(this.colorSettings_);
    135 
    136     /**
    137      * Component that renders a select box for choosing margin settings.
    138      * @type {!print_preview.MarginSettings}
    139      * @private
    140      */
    141     this.marginSettings_ =
    142         new print_preview.MarginSettings(this.printTicketStore_.marginsType);
    143     this.addChild(this.marginSettings_);
    144 
    145     /**
    146      * Component that renders miscellaneous print options.
    147      * @type {!print_preview.OtherOptionsSettings}
    148      * @private
    149      */
    150     this.otherOptionsSettings_ = new print_preview.OtherOptionsSettings(
    151         this.printTicketStore_.duplex,
    152         this.printTicketStore_.fitToPage,
    153         this.printTicketStore_.cssBackground,
    154         this.printTicketStore_.selectionOnly,
    155         this.printTicketStore_.headerFooter);
    156     this.addChild(this.otherOptionsSettings_);
    157 
    158     /**
    159      * Area of the UI that holds the print preview.
    160      * @type {!print_preview.PreviewArea}
    161      * @private
    162      */
    163     this.previewArea_ = new print_preview.PreviewArea(this.destinationStore_,
    164                                                       this.printTicketStore_,
    165                                                       this.nativeLayer_,
    166                                                       this.documentInfo_);
    167     this.addChild(this.previewArea_);
    168 
    169     /**
    170      * Interface to the Google Cloud Print API. Null if Google Cloud Print
    171      * integration is disabled.
    172      * @type {cloudprint.CloudPrintInterface}
    173      * @private
    174      */
    175     this.cloudPrintInterface_ = null;
    176 
    177     /**
    178      * Whether in kiosk mode where print preview can print automatically without
    179      * user intervention. See http://crbug.com/31395. Print will start when
    180      * both the print ticket has been initialized, and an initial printer has
    181      * been selected.
    182      * @type {boolean}
    183      * @private
    184      */
    185     this.isInKioskAutoPrintMode_ = false;
    186 
    187     /**
    188      * State of the print preview UI.
    189      * @type {print_preview.PrintPreview.UiState_}
    190      * @private
    191      */
    192     this.uiState_ = PrintPreview.UiState_.INITIALIZING;
    193 
    194     /**
    195      * Whether document preview generation is in progress.
    196      * @type {boolean}
    197      * @private
    198      */
    199     this.isPreviewGenerationInProgress_ = true;
    200   };
    201 
    202   /**
    203    * States of the print preview.
    204    * @enum {string}
    205    * @private
    206    */
    207   PrintPreview.UiState_ = {
    208     INITIALIZING: 'initializing',
    209     READY: 'ready',
    210     OPENING_PDF_PREVIEW: 'opening-pdf-preview',
    211     OPENING_NATIVE_PRINT_DIALOG: 'opening-native-print-dialog',
    212     PRINTING: 'printing',
    213     FILE_SELECTION: 'file-selection',
    214     CLOSING: 'closing',
    215     ERROR: 'error'
    216   };
    217 
    218   PrintPreview.prototype = {
    219     __proto__: print_preview.Component.prototype,
    220 
    221     /** Sets up the page and print preview by getting the printer list. */
    222     initialize: function() {
    223       this.decorate($('print-preview'));
    224       i18nTemplate.process(document, templateData);
    225       if (!this.previewArea_.hasCompatiblePlugin) {
    226         this.setIsEnabled_(false);
    227       }
    228       this.nativeLayer_.startGetInitialSettings();
    229       cr.ui.FocusOutlineManager.forDocument(document);
    230     },
    231 
    232     /** @override */
    233     enterDocument: function() {
    234       // Native layer events.
    235       this.tracker.add(
    236           this.nativeLayer_,
    237           print_preview.NativeLayer.EventType.INITIAL_SETTINGS_SET,
    238           this.onInitialSettingsSet_.bind(this));
    239       this.tracker.add(
    240           this.nativeLayer_,
    241           print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE,
    242           this.onCloudPrintEnable_.bind(this));
    243       this.tracker.add(
    244           this.nativeLayer_,
    245           print_preview.NativeLayer.EventType.PRINT_TO_CLOUD,
    246           this.onPrintToCloud_.bind(this));
    247       this.tracker.add(
    248           this.nativeLayer_,
    249           print_preview.NativeLayer.EventType.FILE_SELECTION_CANCEL,
    250           this.onFileSelectionCancel_.bind(this));
    251       this.tracker.add(
    252           this.nativeLayer_,
    253           print_preview.NativeLayer.EventType.FILE_SELECTION_COMPLETE,
    254           this.onFileSelectionComplete_.bind(this));
    255       this.tracker.add(
    256           this.nativeLayer_,
    257           print_preview.NativeLayer.EventType.SETTINGS_INVALID,
    258           this.onSettingsInvalid_.bind(this));
    259       this.tracker.add(
    260           this.nativeLayer_,
    261           print_preview.NativeLayer.EventType.DISABLE_SCALING,
    262           this.onDisableScaling_.bind(this));
    263       this.tracker.add(
    264           this.nativeLayer_,
    265           print_preview.NativeLayer.EventType.PRIVET_PRINT_FAILED,
    266           this.onPrivetPrintFailed_.bind(this));
    267 
    268 
    269       this.tracker.add(
    270           $('system-dialog-link'),
    271           'click',
    272           this.openSystemPrintDialog_.bind(this));
    273       this.tracker.add(
    274           $('cloud-print-dialog-link'),
    275           'click',
    276           this.onCloudPrintDialogLinkClick_.bind(this));
    277       this.tracker.add(
    278           $('open-pdf-in-preview-link'),
    279           'click',
    280           this.onOpenPdfInPreviewLinkClick_.bind(this));
    281 
    282       this.tracker.add(
    283           this.previewArea_,
    284           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS,
    285           this.onPreviewGenerationInProgress_.bind(this));
    286       this.tracker.add(
    287           this.previewArea_,
    288           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE,
    289           this.onPreviewGenerationDone_.bind(this));
    290       this.tracker.add(
    291           this.previewArea_,
    292           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_FAIL,
    293           this.onPreviewGenerationFail_.bind(this));
    294       this.tracker.add(
    295           this.previewArea_,
    296           print_preview.PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK,
    297           this.openSystemPrintDialog_.bind(this));
    298 
    299       this.tracker.add(
    300           this.destinationStore_,
    301           print_preview.DestinationStore.EventType.
    302               SELECTED_DESTINATION_CAPABILITIES_READY,
    303           this.printIfReady_.bind(this));
    304       this.tracker.add(
    305           this.destinationStore_,
    306           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
    307           this.onDestinationSelect_.bind(this));
    308       this.tracker.add(
    309           this.destinationStore_,
    310           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
    311           this.onDestinationSearchDone_.bind(this));
    312 
    313       this.tracker.add(
    314           this.printHeader_,
    315           print_preview.PrintHeader.EventType.PRINT_BUTTON_CLICK,
    316           this.onPrintButtonClick_.bind(this));
    317       this.tracker.add(
    318           this.printHeader_,
    319           print_preview.PrintHeader.EventType.CANCEL_BUTTON_CLICK,
    320           this.onCancelButtonClick_.bind(this));
    321 
    322       this.tracker.add(window, 'keydown', this.onKeyDown_.bind(this));
    323 
    324       this.tracker.add(
    325           this.destinationSettings_,
    326           print_preview.DestinationSettings.EventType.CHANGE_BUTTON_ACTIVATE,
    327           this.onDestinationChangeButtonActivate_.bind(this));
    328 
    329       this.tracker.add(
    330           this.destinationSearch_,
    331           print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS,
    332           this.onManageCloudDestinationsActivated_.bind(this));
    333       this.tracker.add(
    334           this.destinationSearch_,
    335           print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS,
    336           this.onManageLocalDestinationsActivated_.bind(this));
    337       this.tracker.add(
    338           this.destinationSearch_,
    339           print_preview.DestinationSearch.EventType.SIGN_IN,
    340           this.onCloudPrintSignInActivated_.bind(this));
    341       this.tracker.add(
    342           this.destinationSearch_,
    343           print_preview.DestinationListItem.EventType.REGISTER_PROMO_CLICKED,
    344           this.onCloudPrintRegisterPromoClick_.bind(this));
    345 
    346       // TODO(rltoscano): Move no-destinations-promo into its own component
    347       // instead being part of PrintPreview.
    348       this.tracker.add(
    349           this.getChildElement('#no-destinations-promo .close-button'),
    350           'click',
    351           this.onNoDestinationsPromoClose_.bind(this));
    352       this.tracker.add(
    353           this.getChildElement('#no-destinations-promo .not-now-button'),
    354           'click',
    355           this.onNoDestinationsPromoClose_.bind(this));
    356       this.tracker.add(
    357           this.getChildElement('#no-destinations-promo .add-printer-button'),
    358           'click',
    359           this.onNoDestinationsPromoClick_.bind(this));
    360     },
    361 
    362     /** @override */
    363     decorateInternal: function() {
    364       this.printHeader_.decorate($('print-header'));
    365       this.destinationSearch_.decorate($('destination-search'));
    366       this.destinationSettings_.decorate($('destination-settings'));
    367       this.pageSettings_.decorate($('page-settings'));
    368       this.copiesSettings_.decorate($('copies-settings'));
    369       this.layoutSettings_.decorate($('layout-settings'));
    370       this.colorSettings_.decorate($('color-settings'));
    371       this.marginSettings_.decorate($('margin-settings'));
    372       this.otherOptionsSettings_.decorate($('other-options-settings'));
    373       this.previewArea_.decorate($('preview-area'));
    374 
    375       setIsVisible($('open-pdf-in-preview-link'), cr.isMac);
    376     },
    377 
    378     /**
    379      * Sets whether the controls in the print preview are enabled.
    380      * @param {boolean} isEnabled Whether the controls in the print preview are
    381      *     enabled.
    382      * @private
    383      */
    384     setIsEnabled_: function(isEnabled) {
    385       $('system-dialog-link').disabled = !isEnabled;
    386       $('cloud-print-dialog-link').disabled = !isEnabled;
    387       $('open-pdf-in-preview-link').disabled = !isEnabled;
    388       this.printHeader_.isEnabled = isEnabled;
    389       this.destinationSettings_.isEnabled = isEnabled;
    390       this.pageSettings_.isEnabled = isEnabled;
    391       this.copiesSettings_.isEnabled = isEnabled;
    392       this.layoutSettings_.isEnabled = isEnabled;
    393       this.colorSettings_.isEnabled = isEnabled;
    394       this.marginSettings_.isEnabled = isEnabled;
    395       this.otherOptionsSettings_.isEnabled = isEnabled;
    396     },
    397 
    398     /**
    399      * Prints the document or launches a pdf preview on the local system.
    400      * @param {boolean} isPdfPreview Whether to launch the pdf preview.
    401      * @private
    402      */
    403     printDocumentOrOpenPdfPreview_: function(isPdfPreview) {
    404       assert(this.uiState_ == PrintPreview.UiState_.READY,
    405              'Print document request received when not in ready state: ' +
    406                  this.uiState_);
    407       if (isPdfPreview) {
    408         this.uiState_ = PrintPreview.UiState_.OPENING_PDF_PREVIEW;
    409       } else if (this.destinationStore_.selectedDestination.id ==
    410           print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
    411         this.uiState_ = PrintPreview.UiState_.FILE_SELECTION;
    412       } else {
    413         this.uiState_ = PrintPreview.UiState_.PRINTING;
    414       }
    415       this.setIsEnabled_(false);
    416       this.printHeader_.isCancelButtonEnabled = true;
    417       if (this.printIfReady_() &&
    418           ((this.destinationStore_.selectedDestination.isLocal &&
    419             !this.destinationStore_.selectedDestination.isPrivet &&
    420             this.destinationStore_.selectedDestination.id !=
    421                 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) ||
    422            this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW)) {
    423         // Hide the dialog for now. The actual print command will be issued when
    424         // the preview generation is done.
    425         this.nativeLayer_.startHideDialog();
    426       }
    427     },
    428 
    429     /**
    430      * Attempts to print if needed and if ready.
    431      * @return {boolean} Whether a print request was issued.
    432      * @private
    433      */
    434     printIfReady_: function() {
    435       if ((this.uiState_ == PrintPreview.UiState_.PRINTING ||
    436               this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW ||
    437               this.uiState_ == PrintPreview.UiState_.FILE_SELECTION ||
    438               this.isInKioskAutoPrintMode_) &&
    439           !this.isPreviewGenerationInProgress_ &&
    440           this.destinationStore_.selectedDestination &&
    441           this.destinationStore_.selectedDestination.capabilities) {
    442         assert(this.printTicketStore_.isTicketValid(),
    443                'Trying to print with invalid ticket');
    444         this.nativeLayer_.startPrint(
    445             this.destinationStore_.selectedDestination,
    446             this.printTicketStore_,
    447             this.cloudPrintInterface_,
    448             this.documentInfo_,
    449             this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW);
    450         return true;
    451       } else {
    452         return false;
    453       }
    454     },
    455 
    456     /**
    457      * Closes the print preview.
    458      * @private
    459      */
    460     close_: function() {
    461       this.exitDocument();
    462       this.uiState_ = PrintPreview.UiState_.CLOSING;
    463       this.nativeLayer_.startCloseDialog();
    464     },
    465 
    466     /**
    467      * Opens the native system print dialog after disabling all controls.
    468      * @private
    469      */
    470     openSystemPrintDialog_: function() {
    471       setIsVisible($('system-dialog-throbber'), true);
    472       this.setIsEnabled_(false);
    473       this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG;
    474       this.nativeLayer_.startShowSystemDialog();
    475     },
    476 
    477     /**
    478      * Called when the native layer has initial settings to set. Sets the
    479      * initial settings of the print preview and begins fetching print
    480      * destinations.
    481      * @param {Event} event Contains the initial print preview settings
    482      *     persisted through the session.
    483      * @private
    484      */
    485     onInitialSettingsSet_: function(event) {
    486       assert(this.uiState_ == PrintPreview.UiState_.INITIALIZING,
    487              'Updating initial settings when not in initializing state: ' +
    488                  this.uiState_);
    489       this.uiState_ = PrintPreview.UiState_.READY;
    490 
    491       var settings = event.initialSettings;
    492       this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode;
    493 
    494       // The following components must be initialized in this order.
    495       this.appState_.init(settings.serializedAppStateStr);
    496       this.documentInfo_.init(
    497           settings.isDocumentModifiable,
    498           settings.documentTitle,
    499           settings.documentHasSelection);
    500       this.printTicketStore_.init(
    501           settings.thousandsDelimeter,
    502           settings.decimalDelimeter,
    503           settings.unitType,
    504           settings.selectionOnly);
    505       this.destinationStore_.init(settings.systemDefaultDestinationId);
    506       this.appState_.setInitialized();
    507 
    508       $('document-title').innerText = settings.documentTitle;
    509       setIsVisible($('system-dialog-link'),
    510                    !settings.hidePrintWithSystemDialogLink);
    511     },
    512 
    513     /**
    514      * Calls when the native layer enables Google Cloud Print integration.
    515      * Fetches the user's cloud printers.
    516      * @param {Event} event Contains the base URL of the Google Cloud Print
    517      *     service.
    518      * @private
    519      */
    520     onCloudPrintEnable_: function(event) {
    521       this.cloudPrintInterface_ =
    522           new cloudprint.CloudPrintInterface(event.baseCloudPrintUrl,
    523                                              this.nativeLayer_);
    524       this.tracker.add(
    525           this.cloudPrintInterface_,
    526           cloudprint.CloudPrintInterface.EventType.SUBMIT_DONE,
    527           this.onCloudPrintSubmitDone_.bind(this));
    528       this.tracker.add(
    529           this.cloudPrintInterface_,
    530           cloudprint.CloudPrintInterface.EventType.SEARCH_FAILED,
    531           this.onCloudPrintError_.bind(this));
    532       this.tracker.add(
    533           this.cloudPrintInterface_,
    534           cloudprint.CloudPrintInterface.EventType.SUBMIT_FAILED,
    535           this.onCloudPrintError_.bind(this));
    536       this.tracker.add(
    537           this.cloudPrintInterface_,
    538           cloudprint.CloudPrintInterface.EventType.PRINTER_FAILED,
    539           this.onCloudPrintError_.bind(this));
    540       this.tracker.add(
    541           this.cloudPrintInterface_,
    542           cloudprint.CloudPrintInterface.EventType.
    543               UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED,
    544           this.onCloudPrintError_.bind(this));
    545 
    546       this.userInfo_.setCloudPrintInterface(this.cloudPrintInterface_);
    547       this.destinationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
    548       if (this.destinationSearch_.getIsVisible()) {
    549         this.destinationStore_.startLoadCloudDestinations();
    550       }
    551     },
    552 
    553     /**
    554      * Called from the native layer when ready to print to Google Cloud Print.
    555      * @param {Event} event Contains the body to send in the HTTP request.
    556      * @private
    557      */
    558     onPrintToCloud_: function(event) {
    559       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
    560              'Document ready to be sent to the cloud when not in printing ' +
    561                  'state: ' + this.uiState_);
    562       assert(this.cloudPrintInterface_ != null,
    563              'Google Cloud Print is not enabled');
    564       this.cloudPrintInterface_.submit(
    565           this.destinationStore_.selectedDestination,
    566           this.printTicketStore_,
    567           this.documentInfo_,
    568           event.data);
    569     },
    570 
    571     /**
    572      * Called from the native layer when the user cancels the save-to-pdf file
    573      * selection dialog.
    574      * @private
    575      */
    576     onFileSelectionCancel_: function() {
    577       assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION,
    578              'File selection cancelled when not in file-selection state: ' +
    579                  this.uiState_);
    580       this.setIsEnabled_(true);
    581       this.uiState_ = PrintPreview.UiState_.READY;
    582     },
    583 
    584     /**
    585      * Called from the native layer when save-to-pdf file selection is complete.
    586      * @private
    587      */
    588     onFileSelectionComplete_: function() {
    589       assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION,
    590              'File selection completed when not in file-selection state: ' +
    591                  this.uiState_);
    592       this.previewArea_.showCustomMessage(
    593           localStrings.getString('printingToPDFInProgress'));
    594       this.uiState_ = PrintPreview.UiState_.PRINTING;
    595     },
    596 
    597     /**
    598      * Called after successfully submitting a job to Google Cloud Print.
    599      * @param {!Event} event Contains the ID of the submitted print job.
    600      * @private
    601      */
    602     onCloudPrintSubmitDone_: function(event) {
    603       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
    604              'Submited job to Google Cloud Print but not in printing state ' +
    605                  this.uiState_);
    606       if (this.destinationStore_.selectedDestination.id ==
    607               print_preview.Destination.GooglePromotedId.FEDEX) {
    608         this.nativeLayer_.startForceOpenNewTab(
    609             'https://www.google.com/cloudprint/fedexcode.html?jobid=' +
    610             event.jobId);
    611       }
    612       this.close_();
    613     },
    614 
    615     /**
    616      * Called when there was an error communicating with Google Cloud print.
    617      * Displays an error message in the print header.
    618      * @param {!Event} event Contains the error message.
    619      * @private
    620      */
    621     onCloudPrintError_: function(event) {
    622       if (event.status == 403) {
    623         this.destinationSearch_.showCloudPrintPromo();
    624       } else if (event.status == 0) {
    625         return; // Ignore, the system does not have internet connectivity.
    626       } else {
    627         this.printHeader_.setErrorMessage(event.message);
    628       }
    629       if (event.status == 200) {
    630         console.error('Google Cloud Print Error: (' + event.errorCode + ') ' +
    631                       event.message);
    632       } else {
    633         console.error('Google Cloud Print Error: HTTP status ' + event.status);
    634       }
    635     },
    636 
    637     /**
    638      * Called when the preview area's preview generation is in progress.
    639      * @private
    640      */
    641     onPreviewGenerationInProgress_: function() {
    642       this.isPreviewGenerationInProgress_ = true;
    643     },
    644 
    645     /**
    646      * Called when the preview area's preview generation is complete.
    647      * @private
    648      */
    649     onPreviewGenerationDone_: function() {
    650       this.isPreviewGenerationInProgress_ = false;
    651       this.printHeader_.isPrintButtonEnabled = true;
    652       this.printIfReady_();
    653     },
    654 
    655     /**
    656      * Called when the preview area's preview failed to load.
    657      * @private
    658      */
    659     onPreviewGenerationFail_: function() {
    660       this.isPreviewGenerationInProgress_ = false;
    661       this.printHeader_.isPrintButtonEnabled = false;
    662       if (this.uiState_ == PrintPreview.UiState_.PRINTING) {
    663         this.nativeLayer_.startCancelPendingPrint();
    664       }
    665     },
    666 
    667     /**
    668      * Called when the 'Open pdf in preview' link is clicked. Launches the pdf
    669      * preview app.
    670      * @private
    671      */
    672     onOpenPdfInPreviewLinkClick_: function() {
    673       assert(this.uiState_ == PrintPreview.UiState_.READY,
    674              'Trying to open pdf in preview when not in ready state: ' +
    675                  this.uiState_);
    676       setIsVisible($('open-preview-app-throbber'), true);
    677       this.previewArea_.showCustomMessage(
    678           localStrings.getString('openingPDFInPreview'));
    679       this.printDocumentOrOpenPdfPreview_(true /*isPdfPreview*/);
    680     },
    681 
    682     /**
    683      * Called when the print header's print button is clicked. Prints the
    684      * document.
    685      * @private
    686      */
    687     onPrintButtonClick_: function() {
    688       assert(this.uiState_ == PrintPreview.UiState_.READY,
    689              'Trying to print when not in ready state: ' + this.uiState_);
    690       this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
    691     },
    692 
    693     /**
    694      * Called when the print header's cancel button is clicked. Closes the
    695      * print dialog.
    696      * @private
    697      */
    698     onCancelButtonClick_: function() {
    699       this.close_();
    700     },
    701 
    702     /**
    703      * Called when the register promo for Cloud Print is clicked.
    704      * @private
    705      */
    706      onCloudPrintRegisterPromoClick_: function(e) {
    707        var devicesUrl = 'chrome://devices/register?id=' + e.destination.id;
    708        this.nativeLayer_.startForceOpenNewTab(devicesUrl);
    709      },
    710 
    711     /**
    712      * Consume escape key presses and ctrl + shift + p. Delegate everything else
    713      * to the preview area.
    714      * @param {KeyboardEvent} e The keyboard event.
    715      * @private
    716      */
    717     onKeyDown_: function(e) {
    718       // Escape key closes the dialog.
    719       if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey &&
    720           !e.metaKey) {
    721         if (this.destinationSearch_.getIsVisible()) {
    722           this.destinationSearch_.setIsVisible(false);
    723           this.metrics_.incrementDestinationSearchBucket(
    724               print_preview.Metrics.DestinationSearchBucket.CANCELED);
    725         } else {
    726           // <if expr="pp_ifdef('toolkit_views')">
    727           // On the toolkit_views environment, ESC key is handled by C++-side
    728           // instead of JS-side.
    729           return;
    730           // </if>
    731           // <if expr="not pp_ifdef('toolkit_views')">
    732           // Dummy comment to absorb previous line's comment symbol.
    733           this.close_();
    734           // </if>
    735         }
    736         e.preventDefault();
    737         return;
    738       }
    739 
    740       // Ctrl + Shift + p / Mac equivalent.
    741       if (e.keyCode == 80) {
    742         if ((cr.isMac && e.metaKey && e.altKey && !e.shiftKey && !e.ctrlKey) ||
    743             (!cr.isMac && e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey)) {
    744           this.openSystemPrintDialog_();
    745           e.preventDefault();
    746           return;
    747         }
    748       }
    749 
    750       if (e.keyCode == 13 /*enter*/ &&
    751           !this.destinationSearch_.getIsVisible() &&
    752           this.printTicketStore_.isTicketValid()) {
    753         assert(this.uiState_ == PrintPreview.UiState_.READY,
    754           'Trying to print when not in ready state: ' + this.uiState_);
    755         this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
    756         e.preventDefault();
    757         return;
    758       }
    759 
    760       // Pass certain directional keyboard events to the PDF viewer.
    761       this.previewArea_.handleDirectionalKeyEvent(e);
    762     },
    763 
    764     /**
    765      * Called when native layer receives invalid settings for a print request.
    766      * @private
    767      */
    768     onSettingsInvalid_: function() {
    769       this.uiState_ = PrintPreview.UiState_.ERROR;
    770       console.error('Invalid settings error reported from native layer');
    771       this.previewArea_.showCustomMessage(
    772           localStrings.getString('invalidPrinterSettings'));
    773     },
    774 
    775     /**
    776      * Called when the destination settings' change button is activated.
    777      * Displays the destination search component.
    778      * @private
    779      */
    780     onDestinationChangeButtonActivate_: function() {
    781       this.destinationSearch_.setIsVisible(true);
    782       this.destinationStore_.startLoadCloudDestinations();
    783       this.destinationStore_.startLoadLocalDestinations();
    784       this.destinationStore_.startLoadPrivetDestinations();
    785       this.metrics_.incrementDestinationSearchBucket(
    786           print_preview.Metrics.DestinationSearchBucket.SHOWN);
    787     },
    788 
    789     /**
    790      * Called when the destination search dispatches manage cloud destinations
    791      * event. Calls corresponding native layer method.
    792      * @private
    793      */
    794     onManageCloudDestinationsActivated_: function() {
    795       this.nativeLayer_.startManageCloudDestinations();
    796     },
    797 
    798     /**
    799      * Called when the destination search dispatches manage local destinations
    800      * event. Calls corresponding native layer method.
    801      * @private
    802      */
    803     onManageLocalDestinationsActivated_: function() {
    804       this.nativeLayer_.startManageLocalDestinations();
    805     },
    806 
    807     /**
    808      * Called when the user wants to sign in to Google Cloud Print. Calls the
    809      * corresponding native layer event.
    810      * @private
    811      */
    812     onCloudPrintSignInActivated_: function() {
    813       this.nativeLayer_.startCloudPrintSignIn();
    814     },
    815 
    816     /**
    817      * Called when the native layer dispatches a DISABLE_SCALING event. Resets
    818      * fit-to-page selection and updates document info.
    819      * @private
    820      */
    821     onDisableScaling_: function() {
    822       this.printTicketStore_.fitToPage.updateValue(null);
    823       this.documentInfo_.updateIsScalingDisabled(true);
    824     },
    825 
    826     /**
    827      * Called when privet printing fails.
    828      * @param {Event} event Event object representing the failure.
    829      * @private
    830      */
    831     onPrivetPrintFailed_: function(event) {
    832       console.error('Privet printing failed with error code ' +
    833                     event.httpError);
    834       this.printHeader_.setErrorMessage(
    835         localStrings.getString('couldNotPrint'));
    836     },
    837 
    838     /**
    839      * Called when the open-cloud-print-dialog link is clicked. Opens the Google
    840      * Cloud Print web dialog.
    841      * @private
    842      */
    843     onCloudPrintDialogLinkClick_: function() {
    844       assert(this.uiState_ == PrintPreview.UiState_.READY,
    845              'Opening Google Cloud Print dialog when not in ready state: ' +
    846                  this.uiState_);
    847       setIsVisible($('cloud-print-dialog-throbber'), true);
    848       this.setIsEnabled_(false);
    849       this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG;
    850       this.nativeLayer_.startShowCloudPrintDialog(
    851           this.printTicketStore_.pageRange.getPageNumberSet().size);
    852     },
    853 
    854     /**
    855      * Called when a print destination is selected. Shows/hides the "Print with
    856      * Cloud Print" link in the navbar.
    857      * @private
    858      */
    859     onDestinationSelect_: function() {
    860       var selectedDest = this.destinationStore_.selectedDestination;
    861       setIsVisible($('cloud-print-dialog-link'),
    862                    !cr.isChromeOS && !selectedDest.isLocal);
    863     },
    864 
    865     /**
    866      * Called when the destination store loads a group of destinations. Shows
    867      * a promo on Chrome OS if the user has no print destinations promoting
    868      * Google Cloud Print.
    869      * @private
    870      */
    871     onDestinationSearchDone_: function() {
    872       var isPromoVisible = cr.isChromeOS &&
    873           this.cloudPrintInterface_ &&
    874           this.userInfo_.getUserEmail() &&
    875           !this.appState_.isGcpPromoDismissed &&
    876           !this.destinationStore_.isLocalDestinationsSearchInProgress &&
    877           !this.destinationStore_.isCloudDestinationsSearchInProgress &&
    878           this.destinationStore_.hasOnlyDefaultCloudDestinations();
    879       setIsVisible(this.getChildElement('#no-destinations-promo'),
    880                    isPromoVisible);
    881       if (isPromoVisible) {
    882         this.metrics_.incrementGcpPromoBucket(
    883             print_preview.Metrics.GcpPromoBucket.SHOWN);
    884       }
    885     },
    886 
    887     /**
    888      * Called when the close button on the no-destinations-promotion is clicked.
    889      * Hides the promotion.
    890      * @private
    891      */
    892     onNoDestinationsPromoClose_: function() {
    893       this.metrics_.incrementGcpPromoBucket(
    894           print_preview.Metrics.GcpPromoBucket.DISMISSED);
    895       setIsVisible(this.getChildElement('#no-destinations-promo'), false);
    896       this.appState_.persistIsGcpPromoDismissed(true);
    897     },
    898 
    899     /**
    900      * Called when the no-destinations promotion link is clicked. Opens the
    901      * Google Cloud Print management page and closes the print preview.
    902      * @private
    903      */
    904     onNoDestinationsPromoClick_: function() {
    905       this.metrics_.incrementGcpPromoBucket(
    906           print_preview.Metrics.GcpPromoBucket.CLICKED);
    907       this.appState_.persistIsGcpPromoDismissed(true);
    908       window.open(this.cloudPrintInterface_.baseUrl + '?user=' +
    909                   this.userInfo_.getUserEmail() + '#printers');
    910       this.close_();
    911     }
    912   };
    913 
    914   // Export
    915   return {
    916     PrintPreview: PrintPreview
    917   };
    918 });
    919 
    920 // Pull in all other scripts in a single shot.
    921 <include src="data/page_number_set.js"/>
    922 <include src="data/destination.js"/>
    923 <include src="data/local_parsers.js"/>
    924 <include src="data/cloud_parsers.js"/>
    925 <include src="data/destination_store.js"/>
    926 <include src="data/margins.js"/>
    927 <include src="data/document_info.js"/>
    928 <include src="data/printable_area.js"/>
    929 <include src="data/measurement_system.js"/>
    930 <include src="data/print_ticket_store.js"/>
    931 <include src="data/coordinate2d.js"/>
    932 <include src="data/size.js"/>
    933 <include src="data/capabilities_holder.js"/>
    934 <include src="data/user_info.js"/>
    935 <include src="data/app_state.js"/>
    936 
    937 <include src="data/ticket_items/ticket_item.js"/>
    938 
    939 <include src="data/ticket_items/custom_margins.js"/>
    940 <include src="data/ticket_items/collate.js"/>
    941 <include src="data/ticket_items/color.js"/>
    942 <include src="data/ticket_items/copies.js"/>
    943 <include src="data/ticket_items/duplex.js"/>
    944 <include src="data/ticket_items/header_footer.js"/>
    945 <include src="data/ticket_items/landscape.js"/>
    946 <include src="data/ticket_items/margins_type.js"/>
    947 <include src="data/ticket_items/page_range.js"/>
    948 <include src="data/ticket_items/fit_to_page.js"/>
    949 <include src="data/ticket_items/css_background.js"/>
    950 <include src="data/ticket_items/selection_only.js"/>
    951 
    952 <include src="native_layer.js"/>
    953 <include src="print_preview_animations.js"/>
    954 <include src="cloud_print_interface.js"/>
    955 <include src="print_preview_utils.js"/>
    956 <include src="print_header.js"/>
    957 <include src="metrics.js"/>
    958 
    959 <include src="settings/page_settings.js"/>
    960 <include src="settings/copies_settings.js"/>
    961 <include src="settings/layout_settings.js"/>
    962 <include src="settings/color_settings.js"/>
    963 <include src="settings/margin_settings.js"/>
    964 <include src="settings/destination_settings.js"/>
    965 <include src="settings/other_options_settings.js"/>
    966 
    967 <include src="previewarea/margin_control.js"/>
    968 <include src="previewarea/margin_control_container.js"/>
    969 <include src="previewarea/preview_area.js"/>
    970 <include src="preview_generator.js"/>
    971 
    972 <include src="search/destination_list.js"/>
    973 <include src="search/cloud_destination_list.js"/>
    974 <include src="search/recent_destination_list.js"/>
    975 <include src="search/destination_list_item.js"/>
    976 <include src="search/destination_search.js"/>
    977 <include src="search/search_box.js"/>
    978 <include src="search/fedex_tos.js"/>
    979 
    980 window.addEventListener('DOMContentLoaded', function() {
    981   printPreview = new print_preview.PrintPreview();
    982   printPreview.initialize();
    983 });
    984