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 cr.define('print_preview', function() {
9 * Component used for searching for a print destination.
10 * This is a modal dialog that allows the user to search and select a
11 * destination to print to. When a destination is selected, it is written to
12 * the destination store.
13 * @param {!print_preview.DestinationStore} destinationStore Data store
14 * containing the destinations to search through.
15 * @param {!print_preview.UserInfo} userInfo Event target that contains
16 * information about the logged in user.
17 * @param {!print_preview.Metrics} metrics Used to record usage statistics.
19 * @extends {print_preview.Component}
21 function DestinationSearch(destinationStore, userInfo, metrics) {
22 print_preview.Component.call(this);
25 * Data store containing the destinations to search through.
26 * @type {!print_preview.DestinationStore}
29 this.destinationStore_ = destinationStore;
32 * Event target that contains information about the logged in user.
33 * @type {!print_preview.UserInfo}
36 this.userInfo_ = userInfo;
39 * Used to record usage statistics.
40 * @type {!print_preview.Metrics}
43 this.metrics_ = metrics;
46 * Search box used to search through the destination lists.
47 * @type {!print_preview.SearchBox}
50 this.searchBox_ = new print_preview.SearchBox();
51 this.addChild(this.searchBox_);
54 * Destination list containing recent destinations.
55 * @type {!print_preview.DestinationList}
58 this.recentList_ = new print_preview.RecentDestinationList(this);
59 this.addChild(this.recentList_);
62 * Destination list containing local destinations.
63 * @type {!print_preview.DestinationList}
66 this.localList_ = new print_preview.DestinationList(
68 localStrings.getString('localDestinationsTitle'),
69 cr.isChromeOS ? null : localStrings.getString('manage'));
70 this.addChild(this.localList_);
73 * Destination list containing cloud destinations.
74 * @type {!print_preview.DestinationList}
77 this.cloudList_ = new print_preview.CloudDestinationList(this);
78 this.addChild(this.cloudList_);
82 * Event types dispatched by the component.
85 DestinationSearch.EventType = {
86 // Dispatched when the user requests to manage their cloud destinations.
87 MANAGE_CLOUD_DESTINATIONS:
88 'print_preview.DestinationSearch.MANAGE_CLOUD_DESTINATIONS',
90 // Dispatched when the user requests to manage their local destinations.
91 MANAGE_LOCAL_DESTINATIONS:
92 'print_preview.DestinationSearch.MANAGE_LOCAL_DESTINATIONS',
94 // Dispatched when the user requests to sign-in to their Google account.
95 SIGN_IN: 'print_preview.DestinationSearch.SIGN_IN'
99 * Padding at the bottom of a destination list in pixels.
104 DestinationSearch.LIST_BOTTOM_PADDING_ = 18;
107 * Number of unregistered destinations that may be promoted to the top.
112 DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_ = 2;
114 DestinationSearch.prototype = {
115 __proto__: print_preview.Component.prototype,
117 /** @return {boolean} Whether the component is visible. */
118 getIsVisible: function() {
119 return !this.getElement().classList.contains('transparent');
122 /** @param {boolean} isVisible Whether the component is visible. */
123 setIsVisible: function(isVisible) {
124 if (this.getIsVisible() == isVisible) {
128 setIsVisible(this.getElement(), true);
129 setTimeout(function(element) {
130 element.classList.remove('transparent');
131 }.bind(this, this.getElement()), 0);
132 this.searchBox_.focus();
133 var promoEl = this.getChildElement('.cloudprint-promo');
134 if (getIsVisible(promoEl)) {
135 this.metrics_.incrementDestinationSearchBucket(
136 print_preview.Metrics.DestinationSearchBucket.
137 CLOUDPRINT_PROMO_SHOWN);
141 this.getElement().classList.add('transparent');
142 // Collapse all destination lists
143 this.localList_.setIsShowAll(false);
144 this.cloudList_.setIsShowAll(false);
145 this.searchBox_.setQuery('');
146 this.filterLists_(null);
150 /** @param {string} email Email of the logged-in user. */
151 setCloudPrintEmail: function(email) {
152 var userInfoEl = this.getChildElement('.user-info');
153 userInfoEl.textContent = localStrings.getStringF('userInfo', email);
154 userInfoEl.title = localStrings.getStringF('userInfo', email);
155 setIsVisible(userInfoEl, true);
156 setIsVisible(this.getChildElement('.cloud-list'), true);
157 setIsVisible(this.getChildElement('.cloudprint-promo'), false);
161 /** Shows the Google Cloud Print promotion banner. */
162 showCloudPrintPromo: function() {
163 setIsVisible(this.getChildElement('.cloudprint-promo'), true);
164 if (this.getIsVisible()) {
165 this.metrics_.incrementDestinationSearchBucket(
166 print_preview.Metrics.DestinationSearchBucket.
167 CLOUDPRINT_PROMO_SHOWN);
173 enterDocument: function() {
174 print_preview.Component.prototype.enterDocument.call(this);
176 this.getElement().addEventListener('webkitTransitionEnd', function f(e) {
177 if (e.target != e.currentTarget || e.propertyName != 'opacity')
179 if (e.target.classList.contains('transparent')) {
180 setIsVisible(e.target, false);
185 this.getChildElement('.page > .close-button'),
187 this.onCloseClick_.bind(this));
190 this.getChildElement('.sign-in'),
192 this.onSignInActivated_.bind(this));
195 this.getChildElement('.cloudprint-promo > .close-button'),
197 this.onCloudprintPromoCloseButtonClick_.bind(this));
200 print_preview.SearchBox.EventType.SEARCH,
201 this.onSearch_.bind(this));
204 print_preview.DestinationListItem.EventType.SELECT,
205 this.onDestinationSelect_.bind(this));
208 this.destinationStore_,
209 print_preview.DestinationStore.EventType.DESTINATIONS_INSERTED,
210 this.onDestinationsInserted_.bind(this));
212 this.destinationStore_,
213 print_preview.DestinationStore.EventType.DESTINATION_SELECT,
214 this.onDestinationStoreSelect_.bind(this));
216 this.destinationStore_,
217 print_preview.DestinationStore.EventType.DESTINATION_SEARCH_STARTED,
218 this.updateThrobbers_.bind(this));
220 this.destinationStore_,
221 print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
222 this.updateThrobbers_.bind(this));
226 print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED,
227 this.onManageLocalDestinationsActivated_.bind(this));
230 print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED,
231 this.onManageCloudDestinationsActivated_.bind(this));
234 this.getElement(), 'click', this.onClick_.bind(this));
236 this.getChildElement('.page'),
237 'webkitAnimationEnd',
238 this.onAnimationEnd_.bind(this));
242 print_preview.UserInfo.EventType.EMAIL_CHANGE,
243 this.onEmailChange_.bind(this));
245 this.tracker.add(window, 'resize', this.onWindowResize_.bind(this));
247 this.updateThrobbers_();
249 // Render any destinations already in the store.
250 this.renderDestinations_();
254 decorateInternal: function() {
255 this.searchBox_.decorate($('search-box'));
256 this.recentList_.render(this.getChildElement('.recent-list'));
257 this.localList_.render(this.getChildElement('.local-list'));
258 this.cloudList_.render(this.getChildElement('.cloud-list'));
259 this.getChildElement('.promo-text').innerHTML = localStrings.getStringF(
260 'cloudPrintPromotion',
261 '<span class="sign-in link-button">',
266 * @return {number} Height available for destination lists, in pixels.
269 getAvailableListsHeight_: function() {
270 var elStyle = window.getComputedStyle(this.getElement());
271 return this.getElement().offsetHeight -
272 parseInt(elStyle.getPropertyValue('padding-top')) -
273 parseInt(elStyle.getPropertyValue('padding-bottom')) -
274 this.getChildElement('.lists').offsetTop -
275 this.getChildElement('.cloudprint-promo').offsetHeight;
279 * Filters all destination lists with the given query.
280 * @param {?string} query Query to filter destination lists by.
283 filterLists_: function(query) {
284 this.recentList_.updateSearchQuery(query);
285 this.localList_.updateSearchQuery(query);
286 this.cloudList_.updateSearchQuery(query);
290 * Resets the search query.
293 resetSearch_: function() {
294 this.searchBox_.setQuery(null);
295 this.filterLists_(null);
299 * Renders all of the destinations in the destination store.
302 renderDestinations_: function() {
303 var recentDestinations = [];
304 var localDestinations = [];
305 var cloudDestinations = [];
306 var unregisteredCloudDestinations = [];
308 this.destinationStore_.destinations.forEach(function(destination) {
309 if (destination.isRecent) {
310 recentDestinations.push(destination);
312 if (destination.isLocal ||
313 destination.origin == print_preview.Destination.Origin.DEVICE) {
314 localDestinations.push(destination);
316 if (destination.connectionStatus ==
317 print_preview.Destination.ConnectionStatus.UNREGISTERED) {
318 unregisteredCloudDestinations.push(destination);
320 cloudDestinations.push(destination);
325 var finalCloudDestinations = unregisteredCloudDestinations.slice(
326 0, DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_).concat(
328 unregisteredCloudDestinations.slice(
329 DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_));
331 this.recentList_.updateDestinations(recentDestinations);
332 this.localList_.updateDestinations(localDestinations);
333 this.cloudList_.updateDestinations(finalCloudDestinations);
337 * Reflows the destination lists according to the available height.
340 reflowLists_: function() {
341 if (!this.getIsVisible()) {
345 var hasCloudList = getIsVisible(this.getChildElement('.cloud-list'));
346 var lists = [this.recentList_, this.localList_];
348 lists.push(this.cloudList_);
351 var availableHeight = this.getAvailableListsHeight_();
352 this.getChildElement('.lists').style.maxHeight = availableHeight + 'px';
354 var maxListLength = lists.reduce(function(prevCount, list) {
355 return Math.max(prevCount, list.getDestinationsCount());
357 for (var i = 1; i <= maxListLength; i++) {
358 var listsHeight = lists.reduce(function(sum, list) {
359 return sum + list.getEstimatedHeightInPixels(i) +
360 DestinationSearch.LIST_BOTTOM_PADDING_;
362 if (listsHeight > availableHeight) {
368 lists.forEach(function(list) {
369 list.updateShortListSize(i);
372 // Set height of the list manually so that search filter doesn't change
374 this.getChildElement('.lists').style.height =
375 lists.reduce(function(sum, list) {
376 return sum + list.getEstimatedHeightInPixels(i) +
377 DestinationSearch.LIST_BOTTOM_PADDING_;
382 * Updates whether the throbbers for the various destination lists should be
386 updateThrobbers_: function() {
387 this.localList_.setIsThrobberVisible(
388 this.destinationStore_.isLocalDestinationSearchInProgress);
389 this.cloudList_.setIsThrobberVisible(
390 this.destinationStore_.isCloudDestinationSearchInProgress);
391 this.recentList_.setIsThrobberVisible(
392 this.destinationStore_.isLocalDestinationSearchInProgress &&
393 this.destinationStore_.isCloudDestinationSearchInProgress);
398 * Called when a destination search should be executed. Filters the
399 * destination lists with the given query.
400 * @param {Event} evt Contains the search query.
403 onSearch_: function(evt) {
404 this.filterLists_(evt.query);
408 * Called when the close button is clicked. Hides the search widget.
411 onCloseClick_: function() {
412 this.setIsVisible(false);
414 this.metrics_.incrementDestinationSearchBucket(
415 print_preview.Metrics.DestinationSearchBucket.CANCELED);
419 * Called when a destination is selected. Clears the search and hides the
421 * @param {Event} evt Contains the selected destination.
424 onDestinationSelect_: function(evt) {
425 this.setIsVisible(false);
427 this.destinationStore_.selectDestination(evt.destination);
428 this.metrics_.incrementDestinationSearchBucket(
429 print_preview.Metrics.DestinationSearchBucket.DESTINATION_SELECTED);
433 * Called when a destination is selected. Selected destination are marked as
434 * recent, so we have to update our recent destinations list.
437 onDestinationStoreSelect_: function() {
438 var destinations = this.destinationStore_.destinations;
439 var recentDestinations = [];
440 destinations.forEach(function(destination) {
441 if (destination.isRecent) {
442 recentDestinations.push(destination);
445 this.recentList_.updateDestinations(recentDestinations);
450 * Called when destinations are inserted into the store. Rerenders
454 onDestinationsInserted_: function() {
455 this.renderDestinations_();
460 * Called when the manage cloud printers action is activated.
463 onManageCloudDestinationsActivated_: function() {
464 cr.dispatchSimpleEvent(
466 print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS);
470 * Called when the manage local printers action is activated.
473 onManageLocalDestinationsActivated_: function() {
474 cr.dispatchSimpleEvent(
476 print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS);
480 * Called when the "Sign in" link on the Google Cloud Print promo is
484 onSignInActivated_: function() {
485 cr.dispatchSimpleEvent(this, DestinationSearch.EventType.SIGN_IN);
486 this.metrics_.incrementDestinationSearchBucket(
487 print_preview.Metrics.DestinationSearchBucket.SIGNIN_TRIGGERED);
491 * Called when the close button on the cloud print promo is clicked. Hides
495 onCloudprintPromoCloseButtonClick_: function() {
496 setIsVisible(this.getChildElement('.cloudprint-promo'), false);
500 * Called when the overlay is clicked. Pulses the page.
501 * @param {Event} event Contains the element that was clicked.
504 onClick_: function(event) {
505 if (event.target == this.getElement()) {
506 this.getChildElement('.page').classList.add('pulse');
511 * Called when an animation ends on the page.
514 onAnimationEnd_: function() {
515 this.getChildElement('.page').classList.remove('pulse');
519 * Called when the user's email field has changed. Updates the UI.
522 onEmailChange_: function() {
523 var userEmail = this.userInfo_.getUserEmail();
525 this.setCloudPrintEmail(userEmail);
530 * Called when the window is resized. Reflows layout of destination lists.
533 onWindowResize_: function() {
540 DestinationSearch: DestinationSearch