• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7var localStrings = new LocalStrings(templateData);
8
9<include src="component.js"/>
10
11cr.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
980window.addEventListener('DOMContentLoaded', function() {
981  printPreview = new print_preview.PrintPreview();
982  printPreview.initialize();
983});
984