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