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 5cr.define('print_preview', function() { 6 'use strict'; 7 8 /** 9 * A data store that stores destinations and dispatches events when the data 10 * store changes. 11 * @param {!print_preview.NativeLayer} nativeLayer Used to fetch local print 12 * destinations. 13 * @param {!print_preview.AppState} appState Application state. 14 * @param {!print_preview.Metrics} metrics Metrics. 15 * @constructor 16 * @extends {cr.EventTarget} 17 */ 18 function DestinationStore(nativeLayer, appState, metrics) { 19 cr.EventTarget.call(this); 20 21 /** 22 * Used to fetch local print destinations. 23 * @type {!print_preview.NativeLayer} 24 * @private 25 */ 26 this.nativeLayer_ = nativeLayer; 27 28 /** 29 * Used to load and persist the selected destination. 30 * @type {!print_preview.AppState} 31 * @private 32 */ 33 this.appState_ = appState; 34 35 /** 36 * Used to track metrics. 37 * @type {!print_preview.AppState} 38 * @private 39 */ 40 this.metrics_ = metrics; 41 42 /** 43 * Internal backing store for the data store. 44 * @type {!Array.<!print_preview.Destination>} 45 * @private 46 */ 47 this.destinations_ = []; 48 49 /** 50 * Cache used for constant lookup of destinations by origin and id. 51 * @type {object.<string, !print_preview.Destination>} 52 * @private 53 */ 54 this.destinationMap_ = {}; 55 56 /** 57 * Currently selected destination. 58 * @type {print_preview.Destination} 59 * @private 60 */ 61 this.selectedDestination_ = null; 62 63 /** 64 * Initial destination ID used to auto-select the first inserted destination 65 * that matches. If {@code null}, the first destination inserted into the 66 * store will be selected. 67 * @type {?string} 68 * @private 69 */ 70 this.initialDestinationId_ = null; 71 72 /** 73 * Initial origin used to auto-select destination. 74 * @type {print_preview.Destination.Origin} 75 * @private 76 */ 77 this.initialDestinationOrigin_ = print_preview.Destination.Origin.LOCAL; 78 79 /** 80 * Whether the destination store will auto select the destination that 81 * matches the initial destination. 82 * @type {boolean} 83 * @private 84 */ 85 this.isInAutoSelectMode_ = false; 86 87 /** 88 * Event tracker used to track event listeners of the destination store. 89 * @type {!EventTracker} 90 * @private 91 */ 92 this.tracker_ = new EventTracker(); 93 94 /** 95 * Used to fetch cloud-based print destinations. 96 * @type {print_preview.CloudPrintInterface} 97 * @private 98 */ 99 this.cloudPrintInterface_ = null; 100 101 /** 102 * Whether the destination store has already loaded or is loading all cloud 103 * destinations. 104 * @type {boolean} 105 * @private 106 */ 107 this.hasLoadedAllCloudDestinations_ = false; 108 109 /** 110 * ID of a timeout after the initial destination ID is set. If no inserted 111 * destination matches the initial destination ID after the specified 112 * timeout, the first destination in the store will be automatically 113 * selected. 114 * @type {?number} 115 * @private 116 */ 117 this.autoSelectTimeout_ = null; 118 119 /** 120 * Whether a search for local destinations is in progress. 121 * @type {boolean} 122 * @private 123 */ 124 this.isLocalDestinationSearchInProgress_ = false; 125 126 /** 127 * Whether the destination store has already loaded or is loading all local 128 * destinations. 129 * @type {boolean} 130 * @private 131 */ 132 this.hasLoadedAllLocalDestinations_ = false; 133 134 /** 135 * Whether a search for privet destinations is in progress. 136 * @type {boolean} 137 * @private 138 */ 139 this.isPrivetDestinationSearchInProgress_ = false; 140 141 /** 142 * Whether the destination store has already loaded or is loading all privet 143 * destinations. 144 * @type {boolean} 145 * @private 146 */ 147 this.hasLoadedAllPrivetDestinations_ = false; 148 149 this.addEventListeners_(); 150 this.reset_(); 151 }; 152 153 /** 154 * Event types dispatched by the data store. 155 * @enum {string} 156 */ 157 DestinationStore.EventType = { 158 DESTINATION_SEARCH_DONE: 159 'print_preview.DestinationStore.DESTINATION_SEARCH_DONE', 160 DESTINATION_SEARCH_STARTED: 161 'print_preview.DestinationStore.DESTINATION_SEARCH_STARTED', 162 DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT', 163 DESTINATIONS_INSERTED: 164 'print_preview.DestinationStore.DESTINATIONS_INSERTED', 165 CACHED_SELECTED_DESTINATION_INFO_READY: 166 'print_preview.DestinationStore.CACHED_SELECTED_DESTINATION_INFO_READY', 167 SELECTED_DESTINATION_CAPABILITIES_READY: 168 'print_preview.DestinationStore.SELECTED_DESTINATION_CAPABILITIES_READY' 169 }; 170 171 /** 172 * Delay in milliseconds before the destination store ignores the initial 173 * destination ID and just selects any printer (since the initial destination 174 * was not found). 175 * @type {number} 176 * @const 177 * @private 178 */ 179 DestinationStore.AUTO_SELECT_TIMEOUT_ = 15000; 180 181 /** 182 * Creates a local PDF print destination. 183 * @return {!print_preview.Destination} Created print destination. 184 * @private 185 */ 186 DestinationStore.createLocalPdfPrintDestination_ = function() { 187 var dest = new print_preview.Destination( 188 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF, 189 print_preview.Destination.Type.LOCAL, 190 print_preview.Destination.Origin.LOCAL, 191 localStrings.getString('printToPDF'), 192 false /*isRecent*/, 193 print_preview.Destination.ConnectionStatus.ONLINE); 194 dest.capabilities = { 195 version: '1.0', 196 printer: { 197 page_orientation: { 198 option: [ 199 {type: 'AUTO', is_default: true}, 200 {type: 'PORTRAIT'}, 201 {type: 'LANDSCAPE'} 202 ] 203 }, 204 color: { option: [{type: 'STANDARD_COLOR', is_default: true}] } 205 } 206 }; 207 return dest; 208 }; 209 210 DestinationStore.prototype = { 211 __proto__: cr.EventTarget.prototype, 212 213 /** 214 * @return {!Array.<!print_preview.Destination>} List of destinations in 215 * the store. 216 */ 217 get destinations() { 218 return this.destinations_.slice(0); 219 }, 220 221 /** 222 * @return {print_preview.Destination} The currently selected destination or 223 * {@code null} if none is selected. 224 */ 225 get selectedDestination() { 226 return this.selectedDestination_; 227 }, 228 229 /** 230 * @return {boolean} Whether a search for local destinations is in progress. 231 */ 232 get isLocalDestinationSearchInProgress() { 233 return this.isLocalDestinationSearchInProgress_ || 234 this.isPrivetDestinationSearchInProgress_; 235 }, 236 237 /** 238 * @return {boolean} Whether a search for cloud destinations is in progress. 239 */ 240 get isCloudDestinationSearchInProgress() { 241 return this.cloudPrintInterface_ && 242 this.cloudPrintInterface_.isCloudDestinationSearchInProgress; 243 }, 244 245 /** 246 * Initializes the destination store. Sets the initially selected 247 * destination. If any inserted destinations match this ID, that destination 248 * will be automatically selected. This method must be called after the 249 * print_preview.AppState has been initialized. 250 * @param {?string} systemDefaultDestinationId ID of the system default 251 * destination. 252 * @private 253 */ 254 init: function(systemDefaultDestinationId) { 255 if (this.appState_.selectedDestinationId && 256 this.appState_.selectedDestinationOrigin) { 257 this.initialDestinationId_ = this.appState_.selectedDestinationId; 258 this.initialDestinationOrigin_ = 259 this.appState_.selectedDestinationOrigin; 260 } else if (systemDefaultDestinationId) { 261 this.initialDestinationId_ = systemDefaultDestinationId; 262 this.initialDestinationOrigin_ = print_preview.Destination.Origin.LOCAL; 263 } 264 this.isInAutoSelectMode_ = true; 265 if (!this.initialDestinationId_ || !this.initialDestinationOrigin_) { 266 this.onAutoSelectFailed_(); 267 } else { 268 var key = this.getDestinationKey_(this.initialDestinationOrigin_, 269 this.initialDestinationId_); 270 var candidate = this.destinationMap_[key]; 271 if (candidate != null) { 272 this.selectDestination(candidate); 273 } else if (this.initialDestinationOrigin_ == 274 print_preview.Destination.Origin.LOCAL) { 275 this.nativeLayer_.startGetLocalDestinationCapabilities( 276 this.initialDestinationId_); 277 } else if (this.cloudPrintInterface_ && 278 (this.initialDestinationOrigin_ == 279 print_preview.Destination.Origin.COOKIES || 280 this.initialDestinationOrigin_ == 281 print_preview.Destination.Origin.DEVICE)) { 282 this.cloudPrintInterface_.printer(this.initialDestinationId_, 283 this.initialDestinationOrigin_); 284 } else if (this.initialDestinationOrigin_ == 285 print_preview.Destination.Origin.PRIVET) { 286 // TODO(noamsml): Resolve a specific printer instead of listing all 287 // privet printers in this case. 288 this.nativeLayer_.startGetPrivetDestinations(); 289 290 var destinationName = this.appState_.selectedDestinationName || ''; 291 292 // Create a fake selectedDestination_ that is not actually in the 293 // destination store. When the real destination is created, this 294 // destination will be overwritten. 295 this.selectedDestination_ = new print_preview.Destination( 296 this.initialDestinationId_, 297 print_preview.Destination.Type.LOCAL, 298 print_preview.Destination.Origin.PRIVET, 299 destinationName, 300 false /*isRecent*/, 301 print_preview.Destination.ConnectionStatus.ONLINE); 302 this.selectedDestination_.capabilities = 303 this.appState_.selectedDestinationCapabilities; 304 305 cr.dispatchSimpleEvent( 306 this, 307 DestinationStore.EventType.CACHED_SELECTED_DESTINATION_INFO_READY); 308 309 } else { 310 this.onAutoSelectFailed_(); 311 } 312 } 313 }, 314 315 /** 316 * Sets the destination store's Google Cloud Print interface. 317 * @param {!print_preview.CloudPrintInterface} cloudPrintInterface Interface 318 * to set. 319 */ 320 setCloudPrintInterface: function(cloudPrintInterface) { 321 this.cloudPrintInterface_ = cloudPrintInterface; 322 this.tracker_.add( 323 this.cloudPrintInterface_, 324 cloudprint.CloudPrintInterface.EventType.SEARCH_DONE, 325 this.onCloudPrintSearchDone_.bind(this)); 326 this.tracker_.add( 327 this.cloudPrintInterface_, 328 cloudprint.CloudPrintInterface.EventType.SEARCH_FAILED, 329 this.onCloudPrintSearchFailed_.bind(this)); 330 this.tracker_.add( 331 this.cloudPrintInterface_, 332 cloudprint.CloudPrintInterface.EventType.PRINTER_DONE, 333 this.onCloudPrintPrinterDone_.bind(this)); 334 this.tracker_.add( 335 this.cloudPrintInterface_, 336 cloudprint.CloudPrintInterface.EventType.PRINTER_FAILED, 337 this.onCloudPrintPrinterFailed_.bind(this)); 338 }, 339 340 /** 341 * @return {boolean} Whether only default cloud destinations have been 342 * loaded. 343 */ 344 hasOnlyDefaultCloudDestinations: function() { 345 return this.destinations_.every(function(dest) { 346 return dest.isLocal || 347 dest.id == print_preview.Destination.GooglePromotedId.DOCS || 348 dest.id == print_preview.Destination.GooglePromotedId.FEDEX; 349 }); 350 }, 351 352 /** @param {!print_preview.Destination} Destination to select. */ 353 selectDestination: function(destination) { 354 this.selectedDestination_ = destination; 355 this.selectedDestination_.isRecent = true; 356 this.isInAutoSelectMode_ = false; 357 if (this.autoSelectTimeout_ != null) { 358 clearTimeout(this.autoSelectTimeout_); 359 this.autoSelectTimeout_ = null; 360 } 361 if (destination.id == print_preview.Destination.GooglePromotedId.FEDEX && 362 !destination.isTosAccepted) { 363 assert(this.cloudPrintInterface_ != null, 364 'Selected FedEx Office destination, but Google Cloud Print is ' + 365 'not enabled'); 366 destination.isTosAccepted = true; 367 this.cloudPrintInterface_.updatePrinterTosAcceptance(destination.id, 368 destination.origin, 369 true); 370 } 371 this.appState_.persistSelectedDestination(this.selectedDestination_); 372 373 if (destination.cloudID && 374 this.destinations.some(function(otherDestination) { 375 return otherDestination.cloudID == destination.cloudID && 376 otherDestination != destination; 377 })) { 378 if (destination.isPrivet) { 379 this.metrics_.incrementDestinationSearchBucket( 380 print_preview.Metrics.DestinationSearchBucket. 381 PRIVET_DUPLICATE_SELECTED); 382 } else { 383 this.metrics_.incrementDestinationSearchBucket( 384 print_preview.Metrics.DestinationSearchBucket. 385 CLOUD_DUPLICATE_SELECTED); 386 } 387 } 388 389 cr.dispatchSimpleEvent( 390 this, DestinationStore.EventType.DESTINATION_SELECT); 391 if (destination.capabilities == null) { 392 if (destination.isPrivet) { 393 this.nativeLayer_.startGetPrivetDestinationCapabilities( 394 destination.id); 395 } 396 else if (destination.isLocal) { 397 this.nativeLayer_.startGetLocalDestinationCapabilities( 398 destination.id); 399 } else { 400 assert(this.cloudPrintInterface_ != null, 401 'Selected destination is a cloud destination, but Google ' + 402 'Cloud Print is not enabled'); 403 this.cloudPrintInterface_.printer(destination.id, 404 destination.origin); 405 } 406 } else { 407 cr.dispatchSimpleEvent( 408 this, 409 DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY); 410 } 411 }, 412 413 /** 414 * Inserts a print destination to the data store and dispatches a 415 * DESTINATIONS_INSERTED event. If the destination matches the initial 416 * destination ID, then the destination will be automatically selected. 417 * @param {!print_preview.Destination} destination Print destination to 418 * insert. 419 */ 420 insertDestination: function(destination) { 421 if (this.insertDestination_(destination)) { 422 cr.dispatchSimpleEvent( 423 this, DestinationStore.EventType.DESTINATIONS_INSERTED); 424 if (this.isInAutoSelectMode_ && 425 this.matchInitialDestination_(destination.id, destination.origin)) { 426 this.selectDestination(destination); 427 } 428 } 429 }, 430 431 /** 432 * Inserts multiple print destinations to the data store and dispatches one 433 * DESTINATIONS_INSERTED event. If any of the destinations match the initial 434 * destination ID, then that destination will be automatically selected. 435 * @param {!Array.<print_preview.Destination>} destinations Print 436 * destinations to insert. 437 */ 438 insertDestinations: function(destinations) { 439 var insertedDestination = false; 440 var destinationToAutoSelect = null; 441 destinations.forEach(function(dest) { 442 if (this.insertDestination_(dest)) { 443 insertedDestination = true; 444 if (this.isInAutoSelectMode_ && 445 destinationToAutoSelect == null && 446 this.matchInitialDestination_(dest.id, dest.origin)) { 447 destinationToAutoSelect = dest; 448 } 449 } 450 }, this); 451 if (insertedDestination) { 452 cr.dispatchSimpleEvent( 453 this, DestinationStore.EventType.DESTINATIONS_INSERTED); 454 } 455 if (destinationToAutoSelect != null) { 456 this.selectDestination(destinationToAutoSelect); 457 } 458 }, 459 460 /** 461 * Updates an existing print destination with capabilities and display name 462 * information. If the destination doesn't already exist, it will be added. 463 * @param {!print_preview.Destination} destination Destination to update. 464 * @return {!print_preview.Destination} The existing destination that was 465 * updated or {@code null} if it was the new destination. 466 */ 467 updateDestination: function(destination) { 468 assert(destination.constructor !== Array, 'Single printer expected'); 469 var key = this.getDestinationKey_(destination.origin, destination.id); 470 var existingDestination = this.destinationMap_[key]; 471 if (existingDestination != null) { 472 existingDestination.capabilities = destination.capabilities; 473 } else { 474 this.insertDestination(destination); 475 } 476 477 if (existingDestination == this.selectedDestination_ || 478 destination == this.selectedDestination_) { 479 this.appState_.persistSelectedDestination(this.selectedDestination_); 480 cr.dispatchSimpleEvent( 481 this, 482 DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY); 483 } 484 485 return existingDestination; 486 }, 487 488 /** Initiates loading of local print destinations. */ 489 startLoadLocalDestinations: function() { 490 if (!this.hasLoadedAllLocalDestinations_) { 491 this.hasLoadedAllLocalDestinations_ = true; 492 this.nativeLayer_.startGetLocalDestinations(); 493 this.isLocalDestinationSearchInProgress_ = true; 494 cr.dispatchSimpleEvent( 495 this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); 496 } 497 }, 498 499 /** Initiates loading of privet print destinations. */ 500 startLoadPrivetDestinations: function() { 501 if (!this.hasLoadedAllPrivetDestinations_) { 502 this.isPrivetDestinationSearchInProgress_ = true; 503 this.nativeLayer_.startGetPrivetDestinations(); 504 cr.dispatchSimpleEvent( 505 this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); 506 } 507 }, 508 509 /** 510 * Initiates loading of cloud destinations. 511 */ 512 startLoadCloudDestinations: function() { 513 if (this.cloudPrintInterface_ != null && 514 !this.hasLoadedAllCloudDestinations_) { 515 this.hasLoadedAllCloudDestinations_ = true; 516 this.cloudPrintInterface_.search(true); 517 this.cloudPrintInterface_.search(false); 518 cr.dispatchSimpleEvent( 519 this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); 520 } 521 }, 522 523 /** 524 * Inserts a destination into the store without dispatching any events. 525 * @return {boolean} Whether the inserted destination was not already in the 526 * store. 527 * @private 528 */ 529 insertDestination_: function(destination) { 530 var key = this.getDestinationKey_(destination.origin, destination.id); 531 var existingDestination = this.destinationMap_[key]; 532 if (existingDestination == null) { 533 this.destinations_.push(destination); 534 this.destinationMap_[key] = destination; 535 return true; 536 } else if (existingDestination.connectionStatus == 537 print_preview.Destination.ConnectionStatus.UNKNOWN && 538 destination.connectionStatus != 539 print_preview.Destination.ConnectionStatus.UNKNOWN) { 540 existingDestination.connectionStatus = destination.connectionStatus; 541 return true; 542 } else { 543 return false; 544 } 545 }, 546 547 /** 548 * Binds handlers to events. 549 * @private 550 */ 551 addEventListeners_: function() { 552 this.tracker_.add( 553 this.nativeLayer_, 554 print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET, 555 this.onLocalDestinationsSet_.bind(this)); 556 this.tracker_.add( 557 this.nativeLayer_, 558 print_preview.NativeLayer.EventType.CAPABILITIES_SET, 559 this.onLocalDestinationCapabilitiesSet_.bind(this)); 560 this.tracker_.add( 561 this.nativeLayer_, 562 print_preview.NativeLayer.EventType.GET_CAPABILITIES_FAIL, 563 this.onGetCapabilitiesFail_.bind(this)); 564 this.tracker_.add( 565 this.nativeLayer_, 566 print_preview.NativeLayer.EventType.DESTINATIONS_RELOAD, 567 this.onDestinationsReload_.bind(this)); 568 this.tracker_.add( 569 this.nativeLayer_, 570 print_preview.NativeLayer.EventType.PRIVET_PRINTER_CHANGED, 571 this.onPrivetPrinterAdded_.bind(this)); 572 this.tracker_.add( 573 this.nativeLayer_, 574 print_preview.NativeLayer.EventType.PRIVET_PRINTER_SEARCH_DONE, 575 this.onPrivetPrinterSearchDone_.bind(this)); 576 this.tracker_.add( 577 this.nativeLayer_, 578 print_preview.NativeLayer.EventType.PRIVET_CAPABILITIES_SET, 579 this.onPrivetCapabilitiesSet_.bind(this)); 580 }, 581 582 /** 583 * Resets the state of the destination store to its initial state. 584 * @private 585 */ 586 reset_: function() { 587 this.destinations_ = []; 588 this.destinationMap_ = {}; 589 this.selectedDestination_ = null; 590 this.hasLoadedAllCloudDestinations_ = false; 591 this.hasLoadedAllLocalDestinations_ = false; 592 this.insertDestination( 593 DestinationStore.createLocalPdfPrintDestination_()); 594 this.autoSelectTimeout_ = 595 setTimeout(this.onAutoSelectFailed_.bind(this), 596 DestinationStore.AUTO_SELECT_TIMEOUT_); 597 }, 598 599 /** 600 * Called when the local destinations have been got from the native layer. 601 * @param {Event} Contains the local destinations. 602 * @private 603 */ 604 onLocalDestinationsSet_: function(event) { 605 var localDestinations = event.destinationInfos.map(function(destInfo) { 606 return print_preview.LocalDestinationParser.parse(destInfo); 607 }); 608 this.insertDestinations(localDestinations); 609 this.isLocalDestinationSearchInProgress_ = false; 610 cr.dispatchSimpleEvent( 611 this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); 612 }, 613 614 /** 615 * Called when the native layer retrieves the capabilities for the selected 616 * local destination. Updates the destination with new capabilities if the 617 * destination already exists, otherwise it creates a new destination and 618 * then updates its capabilities. 619 * @param {Event} event Contains the capabilities of the local print 620 * destination. 621 * @private 622 */ 623 onLocalDestinationCapabilitiesSet_: function(event) { 624 var destinationId = event.settingsInfo['printerId']; 625 var key = 626 this.getDestinationKey_(print_preview.Destination.Origin.LOCAL, 627 destinationId); 628 var destination = this.destinationMap_[key]; 629 var capabilities = print_preview.LocalCapabilitiesParser.parse( 630 event.settingsInfo); 631 if (destination) { 632 // In case there were multiple capabilities request for this local 633 // destination, just ignore the later ones. 634 if (destination.capabilities != null) { 635 return; 636 } 637 destination.capabilities = capabilities; 638 } else { 639 // TODO(rltoscano): This makes the assumption that the "deviceName" is 640 // the same as "printerName". We should include the "printerName" in the 641 // response. See http://crbug.com/132831. 642 destination = print_preview.LocalDestinationParser.parse( 643 {deviceName: destinationId, printerName: destinationId}); 644 destination.capabilities = capabilities; 645 this.insertDestination(destination); 646 } 647 if (this.selectedDestination_ && 648 this.selectedDestination_.id == destinationId) { 649 cr.dispatchSimpleEvent(this, 650 DestinationStore.EventType. 651 SELECTED_DESTINATION_CAPABILITIES_READY); 652 } 653 }, 654 655 /** 656 * Called when a request to get a local destination's print capabilities 657 * fails. If the destination is the initial destination, auto-select another 658 * destination instead. 659 * @param {Event} event Contains the destination ID that failed. 660 * @private 661 */ 662 onGetCapabilitiesFail_: function(event) { 663 console.error('Failed to get print capabilities for printer ' + 664 event.destinationId); 665 if (this.isInAutoSelectMode_ && 666 this.matchInitialDestinationStrict_(event.destinationId, 667 event.destinationOrigin)) { 668 assert(this.destinations_.length > 0, 669 'No destinations were loaded when failed to get initial ' + 670 'destination'); 671 this.selectDestination(this.destinations_[0]); 672 } 673 }, 674 675 /** 676 * Called when the /search call completes. Adds the fetched destinations to 677 * the destination store. 678 * @param {Event} event Contains the fetched destinations. 679 * @private 680 */ 681 onCloudPrintSearchDone_: function(event) { 682 this.insertDestinations(event.printers); 683 cr.dispatchSimpleEvent( 684 this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); 685 }, 686 687 /** 688 * Called when the /search call fails. Updates outstanding request count and 689 * dispatches CLOUD_DESTINATIONS_LOADED event. 690 * @private 691 */ 692 onCloudPrintSearchFailed_: function() { 693 cr.dispatchSimpleEvent( 694 this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); 695 }, 696 697 /** 698 * Called when /printer call completes. Updates the specified destination's 699 * print capabilities. 700 * @param {Event} event Contains detailed information about the 701 * destination. 702 * @private 703 */ 704 onCloudPrintPrinterDone_: function(event) { 705 this.updateDestination(event.printer); 706 }, 707 708 /** 709 * Called when the Google Cloud Print interface fails to lookup a 710 * destination. Selects another destination if the failed destination was 711 * the initial destination. 712 * @param {object} event Contains the ID of the destination that was failed 713 * to be looked up. 714 * @private 715 */ 716 onCloudPrintPrinterFailed_: function(event) { 717 if (this.isInAutoSelectMode_ && 718 this.matchInitialDestinationStrict_(event.destinationId, 719 event.destinationOrigin)) { 720 console.error('Could not find initial printer: ' + event.destinationId); 721 assert(this.destinations_.length > 0, 722 'No destinations were loaded when failed to get initial ' + 723 'destination'); 724 this.selectDestination(this.destinations_[0]); 725 } 726 }, 727 728 /** 729 * Called when a Privet printer is added to the local network. 730 * @param {object} event Contains information about the added printer. 731 * @private 732 */ 733 onPrivetPrinterAdded_: function(event) { 734 this.insertDestinations( 735 print_preview.PrivetDestinationParser.parse(event.printer)); 736 }, 737 738 /** 739 * Called when capabilities for a privet printer are set. 740 * @param {object} event Contains the capabilities and printer ID. 741 * @private 742 */ 743 onPrivetCapabilitiesSet_: function(event) { 744 var destinationId = event.printerId; 745 var destinations = 746 print_preview.PrivetDestinationParser.parse(event.printer); 747 destinations.forEach(function(dest) { 748 dest.capabilities = event.capabilities; 749 this.updateDestination(dest); 750 }, this); 751 }, 752 753 /** 754 * Called when the search for Privet printers is done. 755 * @private 756 */ 757 onPrivetPrinterSearchDone_: function() { 758 this.isPrivetDestinationSearchInProgress_ = false; 759 this.hasLoadedAllPrivetDestinations_ = true; 760 cr.dispatchSimpleEvent( 761 this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); 762 }, 763 764 /** 765 * Called from native layer after the user was requested to sign in, and did 766 * so successfully. 767 * @private 768 */ 769 onDestinationsReload_: function() { 770 this.reset_(); 771 this.isInAutoSelectMode_ = true; 772 this.startLoadLocalDestinations(); 773 this.startLoadCloudDestinations(); 774 this.startLoadPrivetDestinations(); 775 }, 776 777 /** 778 * Called when auto-selection fails. Selects the first destination in store. 779 * @private 780 */ 781 onAutoSelectFailed_: function() { 782 this.autoSelectTimeout_ = null; 783 assert(this.destinations_.length > 0, 784 'No destinations were loaded before auto-select timeout expired'); 785 this.selectDestination(this.destinations_[0]); 786 }, 787 788 // TODO(vitalybuka): Remove three next functions replacing Destination.id 789 // and Destination.origin by complex ID. 790 /** 791 * Returns key to be used with {@code destinationMap_}. 792 * @param {!print_preview.Destination.Origin} origin Destination origin. 793 * @return {!string} id Destination id. 794 * @private 795 */ 796 getDestinationKey_: function(origin, id) { 797 return origin + '/' + id; 798 }, 799 800 /** 801 * @param {?string} id Id of the destination. 802 * @param {?string} origin Oring of the destination. 803 * @return {boolean} Whether a initial destination matches provided. 804 * @private 805 */ 806 matchInitialDestination_: function(id, origin) { 807 return this.initialDestinationId_ == null || 808 this.initialDestinationOrigin_ == null || 809 this.matchInitialDestinationStrict_(id, origin); 810 }, 811 812 /** 813 * @param {?string} id Id of the destination. 814 * @param {?string} origin Oring of the destination. 815 * @return {boolean} Whether destination is the same as initial. 816 * @private 817 */ 818 matchInitialDestinationStrict_: function(id, origin) { 819 return id == this.initialDestinationId_ && 820 origin == this.initialDestinationOrigin_; 821 } 822 }; 823 824 // Export 825 return { 826 DestinationStore: DestinationStore 827 }; 828}); 829