Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / print_preview.js
blob96336e85c6eddb2790f0b579cd45c2df9f335193
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.
5 // TODO(rltoscano): Move data/* into print_preview.data namespace
7 <include src="component.js">
8 <include src="print_preview_focus_manager.js">
10 cr.define('print_preview', function() {
11   'use strict';
13   /**
14    * Container class for Chromium's print preview.
15    * @constructor
16    * @extends {print_preview.Component}
17    */
18   function PrintPreview() {
19     print_preview.Component.call(this);
21     /**
22      * Used to communicate with Chromium's print system.
23      * @type {!print_preview.NativeLayer}
24      * @private
25      */
26     this.nativeLayer_ = new print_preview.NativeLayer();
28     /**
29      * Event target that contains information about the logged in user.
30      * @type {!print_preview.UserInfo}
31      * @private
32      */
33     this.userInfo_ = new print_preview.UserInfo();
35     /**
36      * Application state.
37      * @type {!print_preview.AppState}
38      * @private
39      */
40     this.appState_ = new print_preview.AppState();
42     /**
43      * Data model that holds information about the document to print.
44      * @type {!print_preview.DocumentInfo}
45      * @private
46      */
47     this.documentInfo_ = new print_preview.DocumentInfo();
49     /**
50      * Data store which holds print destinations.
51      * @type {!print_preview.DestinationStore}
52      * @private
53      */
54     this.destinationStore_ = new print_preview.DestinationStore(
55         this.nativeLayer_, this.userInfo_, this.appState_);
57     /**
58      * Data store which holds printer sharing invitations.
59      * @type {!print_preview.InvitationStore}
60      * @private
61      */
62     this.invitationStore_ = new print_preview.InvitationStore(this.userInfo_);
64     /**
65      * Storage of the print ticket used to create the print job.
66      * @type {!print_preview.PrintTicketStore}
67      * @private
68      */
69     this.printTicketStore_ = new print_preview.PrintTicketStore(
70         this.destinationStore_, this.appState_, this.documentInfo_);
72     /**
73      * Holds the print and cancel buttons and renders some document statistics.
74      * @type {!print_preview.PrintHeader}
75      * @private
76      */
77     this.printHeader_ = new print_preview.PrintHeader(
78         this.printTicketStore_, this.destinationStore_);
79     this.addChild(this.printHeader_);
81     /**
82      * Component used to search for print destinations.
83      * @type {!print_preview.DestinationSearch}
84      * @private
85      */
86     this.destinationSearch_ = new print_preview.DestinationSearch(
87         this.destinationStore_, this.invitationStore_, this.userInfo_);
88     this.addChild(this.destinationSearch_);
90     /**
91      * Component that renders the print destination.
92      * @type {!print_preview.DestinationSettings}
93      * @private
94      */
95     this.destinationSettings_ = new print_preview.DestinationSettings(
96         this.destinationStore_);
97     this.addChild(this.destinationSettings_);
99     /**
100      * Component that renders UI for entering in page range.
101      * @type {!print_preview.PageSettings}
102      * @private
103      */
104     this.pageSettings_ = new print_preview.PageSettings(
105         this.printTicketStore_.pageRange);
106     this.addChild(this.pageSettings_);
108     /**
109      * Component that renders the copies settings.
110      * @type {!print_preview.CopiesSettings}
111      * @private
112      */
113     this.copiesSettings_ = new print_preview.CopiesSettings(
114         this.printTicketStore_.copies, this.printTicketStore_.collate);
115     this.addChild(this.copiesSettings_);
117     /**
118      * Component that renders the media size settings.
119      * @type {!print_preview.MediaSizeSettings}
120      * @private
121      */
122     this.mediaSizeSettings_ =
123         new print_preview.MediaSizeSettings(this.printTicketStore_.mediaSize);
124     this.addChild(this.mediaSizeSettings_);
126     /**
127      * Component that renders the layout settings.
128      * @type {!print_preview.LayoutSettings}
129      * @private
130      */
131     this.layoutSettings_ =
132         new print_preview.LayoutSettings(this.printTicketStore_.landscape);
133     this.addChild(this.layoutSettings_);
135     /**
136      * Component that renders the color options.
137      * @type {!print_preview.ColorSettings}
138      * @private
139      */
140     this.colorSettings_ =
141         new print_preview.ColorSettings(this.printTicketStore_.color);
142     this.addChild(this.colorSettings_);
144     /**
145      * Component that renders a select box for choosing margin settings.
146      * @type {!print_preview.MarginSettings}
147      * @private
148      */
149     this.marginSettings_ =
150         new print_preview.MarginSettings(this.printTicketStore_.marginsType);
151     this.addChild(this.marginSettings_);
153     /**
154      * Component that renders the DPI settings.
155      * @type {!print_preview.DpiSettings}
156      * @private
157      */
158     this.dpiSettings_ =
159         new print_preview.DpiSettings(this.printTicketStore_.dpi);
160     this.addChild(this.dpiSettings_);
162     /**
163      * Component that renders miscellaneous print options.
164      * @type {!print_preview.OtherOptionsSettings}
165      * @private
166      */
167     this.otherOptionsSettings_ = new print_preview.OtherOptionsSettings(
168         this.printTicketStore_.duplex,
169         this.printTicketStore_.fitToPage,
170         this.printTicketStore_.cssBackground,
171         this.printTicketStore_.selectionOnly,
172         this.printTicketStore_.headerFooter);
173     this.addChild(this.otherOptionsSettings_);
175     /**
176      * Component that renders the advanced options button.
177      * @type {!print_preview.AdvancedOptionsSettings}
178      * @private
179      */
180     this.advancedOptionsSettings_ = new print_preview.AdvancedOptionsSettings(
181         this.printTicketStore_.vendorItems, this.destinationStore_);
182     this.addChild(this.advancedOptionsSettings_);
184     /**
185      * Component used to search for print destinations.
186      * @type {!print_preview.AdvancedSettings}
187      * @private
188      */
189     this.advancedSettings_ = new print_preview.AdvancedSettings(
190         this.printTicketStore_);
191     this.addChild(this.advancedSettings_);
193     var settingsSections = [
194         this.destinationSettings_,
195         this.pageSettings_,
196         this.copiesSettings_,
197         this.mediaSizeSettings_,
198         this.layoutSettings_,
199         this.marginSettings_,
200         this.colorSettings_,
201         this.dpiSettings_,
202         this.otherOptionsSettings_,
203         this.advancedOptionsSettings_];
204     /**
205      * Component representing more/less settings button.
206      * @type {!print_preview.MoreSettings}
207      * @private
208      */
209     this.moreSettings_ = new print_preview.MoreSettings(
210         this.destinationStore_, settingsSections);
211     this.addChild(this.moreSettings_);
213     /**
214      * Area of the UI that holds the print preview.
215      * @type {!print_preview.PreviewArea}
216      * @private
217      */
218     this.previewArea_ = new print_preview.PreviewArea(this.destinationStore_,
219                                                       this.printTicketStore_,
220                                                       this.nativeLayer_,
221                                                       this.documentInfo_);
222     this.addChild(this.previewArea_);
224     /**
225      * Interface to the Google Cloud Print API. Null if Google Cloud Print
226      * integration is disabled.
227      * @type {cloudprint.CloudPrintInterface}
228      * @private
229      */
230     this.cloudPrintInterface_ = null;
232     /**
233      * Whether in kiosk mode where print preview can print automatically without
234      * user intervention. See http://crbug.com/31395. Print will start when
235      * both the print ticket has been initialized, and an initial printer has
236      * been selected.
237      * @type {boolean}
238      * @private
239      */
240     this.isInKioskAutoPrintMode_ = false;
242     /**
243      * Whether Print Preview is in App Kiosk mode, basically, use only printers
244      * available for the device.
245      * @type {boolean}
246      * @private
247      */
248     this.isInAppKioskMode_ = false;
250     /**
251      * Whether Print with System Dialog link should be hidden. Overrides the
252      * default rules for System dialog link visibility.
253      * @type {boolean}
254      * @private
255      */
256     this.hideSystemDialogLink_ = true;
258     /**
259      * State of the print preview UI.
260      * @type {print_preview.PrintPreview.UiState_}
261      * @private
262      */
263     this.uiState_ = PrintPreview.UiState_.INITIALIZING;
265     /**
266      * Whether document preview generation is in progress.
267      * @type {boolean}
268      * @private
269      */
270     this.isPreviewGenerationInProgress_ = true;
272     /**
273      * Whether to show system dialog before next printing.
274      * @type {boolean}
275      * @private
276      */
277     this.showSystemDialogBeforeNextPrint_ = false;
278   };
280   /**
281    * States of the print preview.
282    * @enum {string}
283    * @private
284    */
285   PrintPreview.UiState_ = {
286     INITIALIZING: 'initializing',
287     READY: 'ready',
288     OPENING_PDF_PREVIEW: 'opening-pdf-preview',
289     OPENING_NATIVE_PRINT_DIALOG: 'opening-native-print-dialog',
290     PRINTING: 'printing',
291     FILE_SELECTION: 'file-selection',
292     CLOSING: 'closing',
293     ERROR: 'error'
294   };
296   /**
297    * What can happen when print preview tries to print.
298    * @enum {string}
299    * @private
300    */
301   PrintPreview.PrintAttemptResult_ = {
302     NOT_READY: 'not-ready',
303     PRINTED: 'printed',
304     READY_WAITING_FOR_PREVIEW: 'ready-waiting-for-preview'
305   };
307   PrintPreview.prototype = {
308     __proto__: print_preview.Component.prototype,
310     /** Sets up the page and print preview by getting the printer list. */
311     initialize: function() {
312       this.decorate($('print-preview'));
313       if (!this.previewArea_.hasCompatiblePlugin) {
314         this.setIsEnabled_(false);
315       }
316       this.nativeLayer_.startGetInitialSettings();
317       print_preview.PrintPreviewFocusManager.getInstance().initialize();
318       cr.ui.FocusOutlineManager.forDocument(document);
319     },
321     /** @override */
322     enterDocument: function() {
323       // Native layer events.
324       this.tracker.add(
325           this.nativeLayer_,
326           print_preview.NativeLayer.EventType.INITIAL_SETTINGS_SET,
327           this.onInitialSettingsSet_.bind(this));
328       this.tracker.add(
329           this.nativeLayer_,
330           print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE,
331           this.onCloudPrintEnable_.bind(this));
332       this.tracker.add(
333           this.nativeLayer_,
334           print_preview.NativeLayer.EventType.PRINT_TO_CLOUD,
335           this.onPrintToCloud_.bind(this));
336       this.tracker.add(
337           this.nativeLayer_,
338           print_preview.NativeLayer.EventType.FILE_SELECTION_CANCEL,
339           this.onFileSelectionCancel_.bind(this));
340       this.tracker.add(
341           this.nativeLayer_,
342           print_preview.NativeLayer.EventType.FILE_SELECTION_COMPLETE,
343           this.onFileSelectionComplete_.bind(this));
344       this.tracker.add(
345           this.nativeLayer_,
346           print_preview.NativeLayer.EventType.SETTINGS_INVALID,
347           this.onSettingsInvalid_.bind(this));
348       this.tracker.add(
349           this.nativeLayer_,
350           print_preview.NativeLayer.EventType.PRINT_PRESET_OPTIONS,
351           this.onPrintPresetOptionsFromDocument_.bind(this));
352       this.tracker.add(
353           this.nativeLayer_,
354           print_preview.NativeLayer.EventType.PRIVET_PRINT_FAILED,
355           this.onPrivetPrintFailed_.bind(this));
356       this.tracker.add(
357           this.nativeLayer_,
358           print_preview.NativeLayer.EventType.MANIPULATE_SETTINGS_FOR_TEST,
359           this.onManipulateSettingsForTest_.bind(this));
361       if ($('system-dialog-link')) {
362         this.tracker.add(
363             $('system-dialog-link'),
364             'click',
365             this.openSystemPrintDialog_.bind(this));
366       }
367       if ($('open-pdf-in-preview-link')) {
368         this.tracker.add(
369             $('open-pdf-in-preview-link'),
370             'click',
371             this.onOpenPdfInPreviewLinkClick_.bind(this));
372       }
374       this.tracker.add(
375           this.previewArea_,
376           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS,
377           this.onPreviewGenerationInProgress_.bind(this));
378       this.tracker.add(
379           this.previewArea_,
380           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE,
381           this.onPreviewGenerationDone_.bind(this));
382       this.tracker.add(
383           this.previewArea_,
384           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_FAIL,
385           this.onPreviewGenerationFail_.bind(this));
386       this.tracker.add(
387           this.previewArea_,
388           print_preview.PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK,
389           this.openSystemPrintDialog_.bind(this));
391       this.tracker.add(
392           this.destinationStore_,
393           print_preview.DestinationStore.EventType.
394               SELECTED_DESTINATION_CAPABILITIES_READY,
395           this.printIfReady_.bind(this));
396       this.tracker.add(
397           this.destinationStore_,
398           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
399           this.onDestinationSelect_.bind(this));
400       this.tracker.add(
401           this.destinationStore_,
402           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
403           this.onDestinationSearchDone_.bind(this));
405       this.tracker.add(
406           this.printHeader_,
407           print_preview.PrintHeader.EventType.PRINT_BUTTON_CLICK,
408           this.onPrintButtonClick_.bind(this));
409       this.tracker.add(
410           this.printHeader_,
411           print_preview.PrintHeader.EventType.CANCEL_BUTTON_CLICK,
412           this.onCancelButtonClick_.bind(this));
414       this.tracker.add(window, 'keydown', this.onKeyDown_.bind(this));
415       this.previewArea_.setPluginKeyEventCallback(this.onKeyDown_.bind(this));
417       this.tracker.add(
418           this.destinationSettings_,
419           print_preview.DestinationSettings.EventType.CHANGE_BUTTON_ACTIVATE,
420           this.onDestinationChangeButtonActivate_.bind(this));
422       this.tracker.add(
423           this.destinationSearch_,
424           print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS,
425           this.onManageCloudDestinationsActivated_.bind(this));
426       this.tracker.add(
427           this.destinationSearch_,
428           print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS,
429           this.onManageLocalDestinationsActivated_.bind(this));
430       this.tracker.add(
431           this.destinationSearch_,
432           print_preview.DestinationSearch.EventType.ADD_ACCOUNT,
433           this.onCloudPrintSignInActivated_.bind(this, true /*addAccount*/));
434       this.tracker.add(
435           this.destinationSearch_,
436           print_preview.DestinationSearch.EventType.SIGN_IN,
437           this.onCloudPrintSignInActivated_.bind(this, false /*addAccount*/));
438       this.tracker.add(
439           this.destinationSearch_,
440           print_preview.DestinationListItem.EventType.REGISTER_PROMO_CLICKED,
441           this.onCloudPrintRegisterPromoClick_.bind(this));
443       this.tracker.add(
444           this.advancedOptionsSettings_,
445           print_preview.AdvancedOptionsSettings.EventType.BUTTON_ACTIVATED,
446           this.onAdvancedOptionsButtonActivated_.bind(this));
448       // TODO(rltoscano): Move no-destinations-promo into its own component
449       // instead being part of PrintPreview.
450       this.tracker.add(
451           this.getChildElement('#no-destinations-promo .close-button'),
452           'click',
453           this.onNoDestinationsPromoClose_.bind(this));
454       this.tracker.add(
455           this.getChildElement('#no-destinations-promo .not-now-button'),
456           'click',
457           this.onNoDestinationsPromoClose_.bind(this));
458       this.tracker.add(
459           this.getChildElement('#no-destinations-promo .add-printer-button'),
460           'click',
461           this.onNoDestinationsPromoClick_.bind(this));
462     },
464     /** @override */
465     decorateInternal: function() {
466       this.printHeader_.decorate($('print-header'));
467       this.destinationSearch_.decorate($('destination-search'));
468       this.destinationSettings_.decorate($('destination-settings'));
469       this.pageSettings_.decorate($('page-settings'));
470       this.copiesSettings_.decorate($('copies-settings'));
471       this.mediaSizeSettings_.decorate($('media-size-settings'));
472       this.layoutSettings_.decorate($('layout-settings'));
473       this.colorSettings_.decorate($('color-settings'));
474       this.marginSettings_.decorate($('margin-settings'));
475       this.dpiSettings_.decorate($('dpi-settings'));
476       this.otherOptionsSettings_.decorate($('other-options-settings'));
477       this.advancedOptionsSettings_.decorate($('advanced-options-settings'));
478       this.advancedSettings_.decorate($('advanced-settings'));
479       this.moreSettings_.decorate($('more-settings'));
480       this.previewArea_.decorate($('preview-area'));
481     },
483     /**
484      * Sets whether the controls in the print preview are enabled.
485      * @param {boolean} isEnabled Whether the controls in the print preview are
486      *     enabled.
487      * @private
488      */
489     setIsEnabled_: function(isEnabled) {
490       if ($('system-dialog-link'))
491         $('system-dialog-link').classList.toggle('disabled', !isEnabled);
492       if ($('open-pdf-in-preview-link'))
493         $('open-pdf-in-preview-link').classList.toggle('disabled', !isEnabled);
494       this.printHeader_.isEnabled = isEnabled;
495       this.destinationSettings_.isEnabled = isEnabled;
496       this.pageSettings_.isEnabled = isEnabled;
497       this.copiesSettings_.isEnabled = isEnabled;
498       this.mediaSizeSettings_.isEnabled = isEnabled;
499       this.layoutSettings_.isEnabled = isEnabled;
500       this.colorSettings_.isEnabled = isEnabled;
501       this.marginSettings_.isEnabled = isEnabled;
502       this.dpiSettings_.isEnabled = isEnabled;
503       this.otherOptionsSettings_.isEnabled = isEnabled;
504       this.advancedOptionsSettings_.isEnabled = isEnabled;
505     },
507     /**
508      * Prints the document or launches a pdf preview on the local system.
509      * @param {boolean} isPdfPreview Whether to launch the pdf preview.
510      * @private
511      */
512     printDocumentOrOpenPdfPreview_: function(isPdfPreview) {
513       assert(this.uiState_ == PrintPreview.UiState_.READY,
514              'Print document request received when not in ready state: ' +
515                  this.uiState_);
516       if (isPdfPreview) {
517         this.uiState_ = PrintPreview.UiState_.OPENING_PDF_PREVIEW;
518       } else if (this.destinationStore_.selectedDestination.id ==
519           print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
520         this.uiState_ = PrintPreview.UiState_.FILE_SELECTION;
521       } else {
522         this.uiState_ = PrintPreview.UiState_.PRINTING;
523       }
524       this.setIsEnabled_(false);
525       this.printHeader_.isCancelButtonEnabled = true;
526       var printAttemptResult = this.printIfReady_();
527       if (printAttemptResult == PrintPreview.PrintAttemptResult_.PRINTED ||
528           printAttemptResult ==
529               PrintPreview.PrintAttemptResult_.READY_WAITING_FOR_PREVIEW) {
530         if ((this.destinationStore_.selectedDestination.isLocal &&
531              !this.destinationStore_.selectedDestination.isPrivet &&
532              !this.destinationStore_.selectedDestination.isExtension &&
533              this.destinationStore_.selectedDestination.id !=
534                  print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) ||
535              this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW) {
536           // Hide the dialog for now. The actual print command will be issued
537           // when the preview generation is done.
538           this.nativeLayer_.startHideDialog();
539         }
540       }
541     },
543     /**
544      * Attempts to print if needed and if ready.
545      * @return {PrintPreview.PrintAttemptResult_} Attempt result.
546      * @private
547      */
548     printIfReady_: function() {
549       var okToPrint =
550           (this.uiState_ == PrintPreview.UiState_.PRINTING ||
551            this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW ||
552            this.uiState_ == PrintPreview.UiState_.FILE_SELECTION ||
553            this.isInKioskAutoPrintMode_) &&
554           this.destinationStore_.selectedDestination &&
555           this.destinationStore_.selectedDestination.capabilities;
556       if (!okToPrint) {
557         return PrintPreview.PrintAttemptResult_.NOT_READY;
558       }
559       if (this.isPreviewGenerationInProgress_) {
560         return PrintPreview.PrintAttemptResult_.READY_WAITING_FOR_PREVIEW;
561       }
562       assert(this.printTicketStore_.isTicketValid(),
563           'Trying to print with invalid ticket');
564       if (getIsVisible(this.moreSettings_.getElement())) {
565         new print_preview.PrintSettingsUiMetricsContext().record(
566             this.moreSettings_.isExpanded ?
567                 print_preview.Metrics.PrintSettingsUiBucket.
568                     PRINT_WITH_SETTINGS_EXPANDED :
569                 print_preview.Metrics.PrintSettingsUiBucket.
570                     PRINT_WITH_SETTINGS_COLLAPSED);
571       }
572       this.nativeLayer_.startPrint(
573           this.destinationStore_.selectedDestination,
574           this.printTicketStore_,
575           this.cloudPrintInterface_,
576           this.documentInfo_,
577           this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW,
578           this.showSystemDialogBeforeNextPrint_);
579       this.showSystemDialogBeforeNextPrint_ = false;
580       return PrintPreview.PrintAttemptResult_.PRINTED;
581     },
583     /**
584      * Closes the print preview.
585      * @private
586      */
587     close_: function() {
588       this.exitDocument();
589       this.uiState_ = PrintPreview.UiState_.CLOSING;
590       this.nativeLayer_.startCloseDialog();
591     },
593     /**
594      * Opens the native system print dialog after disabling all controls.
595      * @private
596      */
597     openSystemPrintDialog_: function() {
598       if (!this.shouldShowSystemDialogLink_())
599         return;
600       if ($('system-dialog-link').classList.contains('disabled'))
601         return;
602       if (cr.isWindows) {
603         this.showSystemDialogBeforeNextPrint_ = true;
604         this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
605         return;
606       }
607       setIsVisible(getRequiredElement('system-dialog-throbber'), true);
608       this.setIsEnabled_(false);
609       this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG;
610       this.nativeLayer_.startShowSystemDialog();
611     },
613     /**
614      * Called when the native layer has initial settings to set. Sets the
615      * initial settings of the print preview and begins fetching print
616      * destinations.
617      * @param {Event} event Contains the initial print preview settings
618      *     persisted through the session.
619      * @private
620      */
621     onInitialSettingsSet_: function(event) {
622       assert(this.uiState_ == PrintPreview.UiState_.INITIALIZING,
623              'Updating initial settings when not in initializing state: ' +
624                  this.uiState_);
625       this.uiState_ = PrintPreview.UiState_.READY;
627       var settings = event.initialSettings;
628       this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode;
629       this.isInAppKioskMode_ = settings.isInAppKioskMode;
631       // The following components must be initialized in this order.
632       this.appState_.init(
633           settings.serializedAppStateStr,
634           settings.systemDefaultDestinationId);
635       this.documentInfo_.init(
636           settings.isDocumentModifiable,
637           settings.documentTitle,
638           settings.documentHasSelection);
639       this.printTicketStore_.init(
640           settings.thousandsDelimeter,
641           settings.decimalDelimeter,
642           settings.unitType,
643           settings.selectionOnly);
644       this.destinationStore_.init(settings.isInAppKioskMode);
645       this.appState_.setInitialized();
647       $('document-title').innerText = settings.documentTitle;
648       this.hideSystemDialogLink_ = settings.hidePrintWithSystemDialogLink ||
649                                    settings.isInAppKioskMode;
650       if ($('system-dialog-link')) {
651         setIsVisible($('system-dialog-link'),
652                      this.shouldShowSystemDialogLink_());
653       }
654     },
656     /**
657      * Calls when the native layer enables Google Cloud Print integration.
658      * Fetches the user's cloud printers.
659      * @param {Event} event Contains the base URL of the Google Cloud Print
660      *     service.
661      * @private
662      */
663     onCloudPrintEnable_: function(event) {
664       this.cloudPrintInterface_ = new cloudprint.CloudPrintInterface(
665           event.baseCloudPrintUrl,
666           this.nativeLayer_,
667           this.userInfo_,
668           event.appKioskMode);
669       this.tracker.add(
670           this.cloudPrintInterface_,
671           cloudprint.CloudPrintInterface.EventType.SUBMIT_DONE,
672           this.onCloudPrintSubmitDone_.bind(this));
673       this.tracker.add(
674           this.cloudPrintInterface_,
675           cloudprint.CloudPrintInterface.EventType.SEARCH_FAILED,
676           this.onCloudPrintError_.bind(this));
677       this.tracker.add(
678           this.cloudPrintInterface_,
679           cloudprint.CloudPrintInterface.EventType.SUBMIT_FAILED,
680           this.onCloudPrintError_.bind(this));
681       this.tracker.add(
682           this.cloudPrintInterface_,
683           cloudprint.CloudPrintInterface.EventType.PRINTER_FAILED,
684           this.onCloudPrintError_.bind(this));
685       this.tracker.add(
686           this.cloudPrintInterface_,
687           cloudprint.CloudPrintInterface.EventType.
688               UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED,
689           this.onCloudPrintError_.bind(this));
691       this.destinationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
692       this.invitationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
693       if (this.destinationSearch_.getIsVisible()) {
694         this.destinationStore_.startLoadCloudDestinations();
695         this.invitationStore_.startLoadingInvitations();
696       }
697     },
699     /**
700      * Called from the native layer when ready to print to Google Cloud Print.
701      * @param {Event} event Contains the body to send in the HTTP request.
702      * @private
703      */
704     onPrintToCloud_: function(event) {
705       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
706              'Document ready to be sent to the cloud when not in printing ' +
707                  'state: ' + this.uiState_);
708       assert(this.cloudPrintInterface_ != null,
709              'Google Cloud Print is not enabled');
710       this.cloudPrintInterface_.submit(
711           this.destinationStore_.selectedDestination,
712           this.printTicketStore_,
713           this.documentInfo_,
714           event.data);
715     },
717     /**
718      * Called from the native layer when the user cancels the save-to-pdf file
719      * selection dialog.
720      * @private
721      */
722     onFileSelectionCancel_: function() {
723       assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION,
724              'File selection cancelled when not in file-selection state: ' +
725                  this.uiState_);
726       this.setIsEnabled_(true);
727       this.uiState_ = PrintPreview.UiState_.READY;
728     },
730     /**
731      * Called from the native layer when save-to-pdf file selection is complete.
732      * @private
733      */
734     onFileSelectionComplete_: function() {
735       assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION,
736              'File selection completed when not in file-selection state: ' +
737                  this.uiState_);
738       this.previewArea_.showCustomMessage(
739           loadTimeData.getString('printingToPDFInProgress'));
740       this.uiState_ = PrintPreview.UiState_.PRINTING;
741     },
743     /**
744      * Called after successfully submitting a job to Google Cloud Print.
745      * @param {!Event} event Contains the ID of the submitted print job.
746      * @private
747      */
748     onCloudPrintSubmitDone_: function(event) {
749       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
750              'Submited job to Google Cloud Print but not in printing state ' +
751                  this.uiState_);
752       if (this.destinationStore_.selectedDestination.id ==
753               print_preview.Destination.GooglePromotedId.FEDEX) {
754         this.nativeLayer_.startForceOpenNewTab(
755             'https://www.google.com/cloudprint/fedexcode.html?jobid=' +
756             event.jobId);
757       }
758       this.close_();
759     },
761     /**
762      * Called when there was an error communicating with Google Cloud print.
763      * Displays an error message in the print header.
764      * @param {!Event} event Contains the error message.
765      * @private
766      */
767     onCloudPrintError_: function(event) {
768       if (event.status == 403) {
769         if (!this.isInAppKioskMode_) {
770           this.destinationSearch_.showCloudPrintPromo();
771         }
772       } else if (event.status == 0) {
773         return; // Ignore, the system does not have internet connectivity.
774       } else {
775         this.printHeader_.setErrorMessage(event.message);
776       }
777       if (event.status == 200) {
778         console.error('Google Cloud Print Error: (' + event.errorCode + ') ' +
779                       event.message);
780       } else {
781         console.error('Google Cloud Print Error: HTTP status ' + event.status);
782       }
783     },
785     /**
786      * Called when the preview area's preview generation is in progress.
787      * @private
788      */
789     onPreviewGenerationInProgress_: function() {
790       this.isPreviewGenerationInProgress_ = true;
791     },
793     /**
794      * Called when the preview area's preview generation is complete.
795      * @private
796      */
797     onPreviewGenerationDone_: function() {
798       this.isPreviewGenerationInProgress_ = false;
799       this.printHeader_.isPrintButtonEnabled = true;
800       this.nativeLayer_.previewReadyForTest();
801       this.printIfReady_();
802     },
804     /**
805      * Called when the preview area's preview failed to load.
806      * @private
807      */
808     onPreviewGenerationFail_: function() {
809       this.isPreviewGenerationInProgress_ = false;
810       this.printHeader_.isPrintButtonEnabled = false;
811       if (this.uiState_ == PrintPreview.UiState_.PRINTING)
812         this.nativeLayer_.startCancelPendingPrint();
813     },
815     /**
816      * Called when the 'Open pdf in preview' link is clicked. Launches the pdf
817      * preview app.
818      * @private
819      */
820     onOpenPdfInPreviewLinkClick_: function() {
821       if ($('open-pdf-in-preview-link').classList.contains('disabled'))
822         return;
823       assert(this.uiState_ == PrintPreview.UiState_.READY,
824              'Trying to open pdf in preview when not in ready state: ' +
825                  this.uiState_);
826       setIsVisible(getRequiredElement('open-preview-app-throbber'), true);
827       this.previewArea_.showCustomMessage(
828           loadTimeData.getString('openingPDFInPreview'));
829       this.printDocumentOrOpenPdfPreview_(true /*isPdfPreview*/);
830     },
832     /**
833      * Called when the print header's print button is clicked. Prints the
834      * document.
835      * @private
836      */
837     onPrintButtonClick_: function() {
838       assert(this.uiState_ == PrintPreview.UiState_.READY,
839              'Trying to print when not in ready state: ' + this.uiState_);
840       this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
841     },
843     /**
844      * Called when the print header's cancel button is clicked. Closes the
845      * print dialog.
846      * @private
847      */
848     onCancelButtonClick_: function() {
849       this.close_();
850     },
852     /**
853      * Called when the register promo for Cloud Print is clicked.
854      * @private
855      */
856      onCloudPrintRegisterPromoClick_: function(e) {
857        var devicesUrl = 'chrome://devices/register?id=' + e.destination.id;
858        this.nativeLayer_.startForceOpenNewTab(devicesUrl);
859        this.destinationStore_.waitForRegister(e.destination.id);
860      },
862     /**
863      * Consume escape key presses and ctrl + shift + p. Delegate everything else
864      * to the preview area.
865      * @param {KeyboardEvent} e The keyboard event.
866      * @private
867      * @suppress {uselessCode}
868      * Current compiler preprocessor leaves all the code inside all the <if>s,
869      * so the compiler claims that code after first return is unreachable.
870      */
871     onKeyDown_: function(e) {
872       // Escape key closes the dialog.
873       if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey &&
874           !e.metaKey) {
875         // On non-mac with toolkit-views, ESC key is handled by C++-side instead
876         // of JS-side.
877         if (cr.isMac) {
878           this.close_();
879           e.preventDefault();
880         }
881         return;
882       }
884       // On Mac, Cmd- should close the print dialog.
885       if (cr.isMac && e.keyCode == 189 && e.metaKey) {
886         this.close_();
887         e.preventDefault();
888         return;
889       }
891       // Ctrl + Shift + p / Mac equivalent.
892       if (e.keyCode == 80) {
893         if ((cr.isMac && e.metaKey && e.altKey && !e.shiftKey && !e.ctrlKey) ||
894             (!cr.isMac && e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey)) {
895           this.openSystemPrintDialog_();
896           e.preventDefault();
897           return;
898         }
899       }
901       if (e.keyCode == 13 /*enter*/ &&
902           !document.querySelector('.overlay:not([hidden])') &&
903           this.destinationStore_.selectedDestination &&
904           this.printTicketStore_.isTicketValid() &&
905           this.printHeader_.isPrintButtonEnabled) {
906         assert(this.uiState_ == PrintPreview.UiState_.READY,
907             'Trying to print when not in ready state: ' + this.uiState_);
908         var activeElementTag = document.activeElement.tagName.toUpperCase();
909         if (activeElementTag != 'BUTTON' && activeElementTag != 'SELECT' &&
910             activeElementTag != 'A') {
911           this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
912           e.preventDefault();
913         }
914         return;
915       }
917       // Pass certain directional keyboard events to the PDF viewer.
918       this.previewArea_.handleDirectionalKeyEvent(e);
919     },
921     /**
922      * Called when native layer receives invalid settings for a print request.
923      * @private
924      */
925     onSettingsInvalid_: function() {
926       this.uiState_ = PrintPreview.UiState_.ERROR;
927       console.error('Invalid settings error reported from native layer');
928       this.previewArea_.showCustomMessage(
929           loadTimeData.getString('invalidPrinterSettings'));
930     },
932     /**
933      * Called when the destination settings' change button is activated.
934      * Displays the destination search component.
935      * @private
936      */
937     onDestinationChangeButtonActivate_: function() {
938       this.destinationSearch_.setIsVisible(true);
939     },
941     /**
942      * Called when the destination settings' change button is activated.
943      * Displays the destination search component.
944      * @private
945      */
946     onAdvancedOptionsButtonActivated_: function() {
947       this.advancedSettings_.showForDestination(
948           assert(this.destinationStore_.selectedDestination));
949     },
951     /**
952      * Called when the destination search dispatches manage cloud destinations
953      * event. Calls corresponding native layer method.
954      * @private
955      */
956     onManageCloudDestinationsActivated_: function() {
957       this.nativeLayer_.startManageCloudDestinations(this.userInfo_.activeUser);
958     },
960     /**
961      * Called when the destination search dispatches manage local destinations
962      * event. Calls corresponding native layer method.
963      * @private
964      */
965     onManageLocalDestinationsActivated_: function() {
966       this.nativeLayer_.startManageLocalDestinations();
967     },
969     /**
970      * Called when the user wants to sign in to Google Cloud Print. Calls the
971      * corresponding native layer event.
972      * @param {boolean} addAccount Whether to open an 'add a new account' or
973      *     default sign in page.
974      * @private
975      */
976     onCloudPrintSignInActivated_: function(addAccount) {
977       this.nativeLayer_.startCloudPrintSignIn(addAccount);
978     },
980     /**
981      * Updates printing options according to source document presets.
982      * @param {Event} event Contains options from source document.
983      * @private
984      */
985     onPrintPresetOptionsFromDocument_: function(event) {
986       if (event.optionsFromDocument.disableScaling) {
987         this.printTicketStore_.fitToPage.updateValue(null);
988         this.documentInfo_.updateIsScalingDisabled(true);
989       }
991       if (event.optionsFromDocument.copies > 0 &&
992           this.printTicketStore_.copies.isCapabilityAvailable()) {
993         this.printTicketStore_.copies.updateValue(
994             event.optionsFromDocument.copies);
995       }
997       if (event.optionsFromDocument.duplex >= 0 &&
998           this.printTicketStore_.duplex.isCapabilityAvailable()) {
999         this.printTicketStore_.duplex.updateValue(
1000             event.optionsFromDocument.duplex);
1001       }
1002     },
1004     /**
1005      * Called when privet printing fails.
1006      * @param {Event} event Event object representing the failure.
1007      * @private
1008      */
1009     onPrivetPrintFailed_: function(event) {
1010       console.error('Privet printing failed with error code ' +
1011                     event.httpError);
1012       this.printHeader_.setErrorMessage(
1013           loadTimeData.getString('couldNotPrint'));
1014     },
1016     /**
1017      * Called when the print preview settings need to be changed for testing.
1018      * @param {Event} event Event object that contains the option that is to
1019      *     be changed and what to set that option.
1020      * @private
1021      */
1022     onManipulateSettingsForTest_: function(event) {
1023       var settings =
1024           /** @type {print_preview.PreviewSettings} */(event.settings);
1025       if ('selectSaveAsPdfDestination' in settings) {
1026         this.saveAsPdfForTest_();  // No parameters.
1027       } else if ('layoutSettings' in settings) {
1028         this.setLayoutSettingsForTest_(settings.layoutSettings.portrait);
1029       } else if ('pageRange' in settings) {
1030         this.setPageRangeForTest_(settings.pageRange);
1031       } else if ('headersAndFooters' in settings) {
1032         this.setHeadersAndFootersForTest_(settings.headersAndFooters);
1033       } else if ('backgroundColorsAndImages' in settings) {
1034         this.setBackgroundColorsAndImagesForTest_(
1035             settings.backgroundColorsAndImages);
1036       } else if ('margins' in settings) {
1037         this.setMarginsForTest_(settings.margins);
1038       }
1039     },
1041     /**
1042      * Called by onManipulateSettingsForTest_(). Sets the print destination
1043      * as a pdf.
1044      * @private
1045      */
1046     saveAsPdfForTest_: function() {
1047       if (this.destinationStore_.selectedDestination &&
1048           print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ==
1049           this.destinationStore_.selectedDestination.id) {
1050         this.nativeLayer_.previewReadyForTest();
1051         return;
1052       }
1054       var destinations = this.destinationStore_.destinations();
1055       var pdfDestination = null;
1056       for (var i = 0; i < destinations.length; i++) {
1057         if (destinations[i].id ==
1058             print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
1059           pdfDestination = destinations[i];
1060           break;
1061         }
1062       }
1064       if (pdfDestination)
1065         this.destinationStore_.selectDestination(pdfDestination);
1066       else
1067         this.nativeLayer_.previewFailedForTest();
1068     },
1070     /**
1071      * Called by onManipulateSettingsForTest_(). Sets the layout settings to
1072      * either portrait or landscape.
1073      * @param {boolean} portrait Whether to use portrait page layout;
1074      *     if false: landscape.
1075      * @private
1076      */
1077     setLayoutSettingsForTest_: function(portrait) {
1078       var combobox = document.querySelector('.layout-settings-select');
1079       if (combobox.value == 'portrait') {
1080         this.nativeLayer_.previewReadyForTest();
1081       } else {
1082         combobox.value = 'landscape';
1083         this.layoutSettings_.onSelectChange_();
1084       }
1085     },
1087     /**
1088      * Called by onManipulateSettingsForTest_(). Sets the page range for
1089      * for the print preview settings.
1090      * @param {string} pageRange Sets the page range to the desired value(s).
1091      *     Ex: "1-5,9" means pages 1 through 5 and page 9 will be printed.
1092      * @private
1093      */
1094     setPageRangeForTest_: function(pageRange) {
1095       var textbox = document.querySelector('.page-settings-custom-input');
1096       if (textbox.value == pageRange) {
1097         this.nativeLayer_.previewReadyForTest();
1098       } else {
1099         textbox.value = pageRange;
1100         document.querySelector('.page-settings-custom-radio').click();
1101       }
1102     },
1104     /**
1105      * Called by onManipulateSettings_(). Checks or unchecks the headers and
1106      * footers option on print preview.
1107      * @param {boolean} headersAndFooters Whether the "Headers and Footers"
1108      *     checkbox should be checked.
1109      * @private
1110      */
1111     setHeadersAndFootersForTest_: function(headersAndFooters) {
1112       var checkbox = document.querySelector('.header-footer-checkbox');
1113       if (headersAndFooters == checkbox.checked)
1114         this.nativeLayer_.previewReadyForTest();
1115       else
1116         checkbox.click();
1117     },
1119     /**
1120      * Called by onManipulateSettings_(). Checks or unchecks the background
1121      * colors and images option on print preview.
1122      * @param {boolean} backgroundColorsAndImages If true, the checkbox should
1123      *     be checked. Otherwise it should be unchecked.
1124      * @private
1125      */
1126     setBackgroundColorsAndImagesForTest_: function(backgroundColorsAndImages) {
1127       var checkbox = document.querySelector('.css-background-checkbox');
1128       if (backgroundColorsAndImages == checkbox.checked)
1129         this.nativeLayer_.previewReadyForTest();
1130       else
1131         checkbox.click();
1132     },
1134     /**
1135      * Called by onManipulateSettings_(). Sets the margin settings
1136      * that are desired. Custom margin settings aren't currently supported.
1137      * @param {number} margins The desired margins combobox index. Must be
1138      *     a valid index or else the test fails.
1139      * @private
1140      */
1141     setMarginsForTest_: function(margins) {
1142       var combobox = document.querySelector('.margin-settings-select');
1143       if (margins == combobox.selectedIndex) {
1144         this.nativeLayer_.previewReadyForTest();
1145       } else if (margins >= 0 && margins < combobox.length) {
1146         combobox.selectedIndex = margins;
1147         this.marginSettings_.onSelectChange_();
1148       } else {
1149         this.nativeLayer_.previewFailedForTest();
1150       }
1151     },
1153     /**
1154      * Returns true if "Print using system dialog" link should be shown for
1155      * current destination.
1156      * @return {boolean} Returns true if link should be shown.
1157      */
1158     shouldShowSystemDialogLink_: function() {
1159       if (cr.isChromeOS || this.hideSystemDialogLink_)
1160         return false;
1161       if (!cr.isWindows)
1162         return true;
1163       var selectedDest = this.destinationStore_.selectedDestination;
1164       return !!selectedDest &&
1165              selectedDest.origin == print_preview.Destination.Origin.LOCAL &&
1166              selectedDest.id !=
1167                  print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
1168     },
1170     /**
1171      * Called when a print destination is selected. Shows/hides the "Print with
1172      * Cloud Print" link in the navbar.
1173      * @private
1174      */
1175     onDestinationSelect_: function() {
1176       if ($('system-dialog-link')) {
1177         setIsVisible($('system-dialog-link'),
1178                      this.shouldShowSystemDialogLink_());
1179       }
1180       if (this.destinationStore_.selectedDestination &&
1181           this.isInKioskAutoPrintMode_) {
1182         this.onPrintButtonClick_();
1183       }
1184     },
1186     /**
1187      * Called when the destination store loads a group of destinations. Shows
1188      * a promo on Chrome OS if the user has no print destinations promoting
1189      * Google Cloud Print.
1190      * @private
1191      */
1192     onDestinationSearchDone_: function() {
1193       var isPromoVisible = cr.isChromeOS &&
1194           this.cloudPrintInterface_ &&
1195           this.userInfo_.activeUser &&
1196           !this.appState_.isGcpPromoDismissed &&
1197           !this.destinationStore_.isLocalDestinationSearchInProgress &&
1198           !this.destinationStore_.isCloudDestinationSearchInProgress &&
1199           this.destinationStore_.hasOnlyDefaultCloudDestinations();
1200       setIsVisible(this.getChildElement('#no-destinations-promo'),
1201                    isPromoVisible);
1202       if (isPromoVisible) {
1203         new print_preview.GcpPromoMetricsContext().record(
1204             print_preview.Metrics.GcpPromoBucket.PROMO_SHOWN);
1205       }
1206     },
1208     /**
1209      * Called when the close button on the no-destinations-promotion is clicked.
1210      * Hides the promotion.
1211      * @private
1212      */
1213     onNoDestinationsPromoClose_: function() {
1214       new print_preview.GcpPromoMetricsContext().record(
1215           print_preview.Metrics.GcpPromoBucket.PROMO_CLOSED);
1216       setIsVisible(this.getChildElement('#no-destinations-promo'), false);
1217       this.appState_.persistIsGcpPromoDismissed(true);
1218     },
1220     /**
1221      * Called when the no-destinations promotion link is clicked. Opens the
1222      * Google Cloud Print management page and closes the print preview.
1223      * @private
1224      */
1225     onNoDestinationsPromoClick_: function() {
1226       new print_preview.GcpPromoMetricsContext().record(
1227           print_preview.Metrics.GcpPromoBucket.PROMO_CLICKED);
1228       this.appState_.persistIsGcpPromoDismissed(true);
1229       window.open(this.cloudPrintInterface_.baseUrl + '?user=' +
1230                   this.userInfo_.activeUser + '#printers');
1231       this.close_();
1232     }
1233   };
1235   // Export
1236   return {
1237     PrintPreview: PrintPreview
1238   };
1241 // Pull in all other scripts in a single shot.
1242 <include src="common/overlay.js">
1243 <include src="common/search_box.js">
1244 <include src="common/search_bubble.js">
1246 <include src="data/page_number_set.js">
1247 <include src="data/destination.js">
1248 <include src="data/local_parsers.js">
1249 <include src="data/cloud_parsers.js">
1250 <include src="data/destination_store.js">
1251 <include src="data/invitation.js">
1252 <include src="data/invitation_store.js">
1253 <include src="data/margins.js">
1254 <include src="data/document_info.js">
1255 <include src="data/printable_area.js">
1256 <include src="data/measurement_system.js">
1257 <include src="data/print_ticket_store.js">
1258 <include src="data/coordinate2d.js">
1259 <include src="data/size.js">
1260 <include src="data/capabilities_holder.js">
1261 <include src="data/user_info.js">
1262 <include src="data/app_state.js">
1264 <include src="data/ticket_items/ticket_item.js">
1266 <include src="data/ticket_items/custom_margins.js">
1267 <include src="data/ticket_items/collate.js">
1268 <include src="data/ticket_items/color.js">
1269 <include src="data/ticket_items/copies.js">
1270 <include src="data/ticket_items/dpi.js">
1271 <include src="data/ticket_items/duplex.js">
1272 <include src="data/ticket_items/header_footer.js">
1273 <include src="data/ticket_items/media_size.js">
1274 <include src="data/ticket_items/landscape.js">
1275 <include src="data/ticket_items/margins_type.js">
1276 <include src="data/ticket_items/page_range.js">
1277 <include src="data/ticket_items/fit_to_page.js">
1278 <include src="data/ticket_items/css_background.js">
1279 <include src="data/ticket_items/selection_only.js">
1280 <include src="data/ticket_items/vendor_items.js">
1282 <include src="native_layer.js">
1283 <include src="print_preview_animations.js">
1284 <include src="cloud_print_interface.js">
1285 <include src="print_preview_utils.js">
1286 <include src="print_header.js">
1287 <include src="metrics.js">
1289 <include src="settings/settings_section.js">
1290 <include src="settings/settings_section_select.js">
1291 <include src="settings/page_settings.js">
1292 <include src="settings/copies_settings.js">
1293 <include src="settings/dpi_settings.js">
1294 <include src="settings/media_size_settings.js">
1295 <include src="settings/layout_settings.js">
1296 <include src="settings/color_settings.js">
1297 <include src="settings/margin_settings.js">
1298 <include src="settings/destination_settings.js">
1299 <include src="settings/other_options_settings.js">
1300 <include src="settings/advanced_options_settings.js">
1301 <include src="settings/advanced_settings/advanced_settings.js">
1302 <include src="settings/advanced_settings/advanced_settings_item.js">
1303 <include src="settings/more_settings.js">
1305 <include src="previewarea/margin_control.js">
1306 <include src="previewarea/margin_control_container.js">
1307 <include src="../pdf/pdf_scripting_api.js">
1308 <include src="previewarea/preview_area.js">
1309 <include src="preview_generator.js">
1311 <include src="search/destination_list.js">
1312 <include src="search/cloud_destination_list.js">
1313 <include src="search/recent_destination_list.js">
1314 <include src="search/destination_list_item.js">
1315 <include src="search/destination_search.js">
1316 <include src="search/fedex_tos.js">
1317 <include src="search/provisional_destination_resolver.js">
1319 window.addEventListener('DOMContentLoaded', function() {
1320   printPreview = new print_preview.PrintPreview();
1321   printPreview.initialize();