Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / print_preview / search / destination_search.js
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 cr.define('print_preview', function() {
6   'use strict';
7
8   /**
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.
18    * @constructor
19    * @extends {print_preview.Component}
20    */
21   function DestinationSearch(destinationStore, userInfo, metrics) {
22     print_preview.Component.call(this);
23
24     /**
25      * Data store containing the destinations to search through.
26      * @type {!print_preview.DestinationStore}
27      * @private
28      */
29     this.destinationStore_ = destinationStore;
30
31     /**
32      * Event target that contains information about the logged in user.
33      * @type {!print_preview.UserInfo}
34      * @private
35      */
36     this.userInfo_ = userInfo;
37
38     /**
39      * Used to record usage statistics.
40      * @type {!print_preview.Metrics}
41      * @private
42      */
43     this.metrics_ = metrics;
44
45     /**
46      * Search box used to search through the destination lists.
47      * @type {!print_preview.SearchBox}
48      * @private
49      */
50     this.searchBox_ = new print_preview.SearchBox();
51     this.addChild(this.searchBox_);
52
53     /**
54      * Destination list containing recent destinations.
55      * @type {!print_preview.DestinationList}
56      * @private
57      */
58     this.recentList_ = new print_preview.RecentDestinationList(this);
59     this.addChild(this.recentList_);
60
61     /**
62      * Destination list containing local destinations.
63      * @type {!print_preview.DestinationList}
64      * @private
65      */
66     this.localList_ = new print_preview.DestinationList(
67         this,
68         localStrings.getString('localDestinationsTitle'),
69         cr.isChromeOS ? null : localStrings.getString('manage'));
70     this.addChild(this.localList_);
71
72     /**
73      * Destination list containing cloud destinations.
74      * @type {!print_preview.DestinationList}
75      * @private
76      */
77     this.cloudList_ = new print_preview.CloudDestinationList(this);
78     this.addChild(this.cloudList_);
79   };
80
81   /**
82    * Event types dispatched by the component.
83    * @enum {string}
84    */
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',
89
90     // Dispatched when the user requests to manage their local destinations.
91     MANAGE_LOCAL_DESTINATIONS:
92         'print_preview.DestinationSearch.MANAGE_LOCAL_DESTINATIONS',
93
94     // Dispatched when the user requests to sign-in to their Google account.
95     SIGN_IN: 'print_preview.DestinationSearch.SIGN_IN'
96   };
97
98   /**
99    * Padding at the bottom of a destination list in pixels.
100    * @type {number}
101    * @const
102    * @private
103    */
104   DestinationSearch.LIST_BOTTOM_PADDING_ = 18;
105
106   /**
107    * Number of unregistered destinations that may be promoted to the top.
108    * @type {number}
109    * @const
110    * @private
111    */
112   DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_ = 2;
113
114   DestinationSearch.prototype = {
115     __proto__: print_preview.Component.prototype,
116
117     /** @return {boolean} Whether the component is visible. */
118     getIsVisible: function() {
119       return !this.getElement().classList.contains('transparent');
120     },
121
122     /** @param {boolean} isVisible Whether the component is visible. */
123     setIsVisible: function(isVisible) {
124       if (this.getIsVisible() == isVisible) {
125         return;
126       }
127       if (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);
138         }
139         this.reflowLists_();
140       } else {
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);
147       }
148     },
149
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);
158       this.reflowLists_();
159     },
160
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);
168       }
169       this.reflowLists_();
170     },
171
172     /** @override */
173     enterDocument: function() {
174       print_preview.Component.prototype.enterDocument.call(this);
175
176       this.getElement().addEventListener('webkitTransitionEnd', function f(e) {
177         if (e.target != e.currentTarget || e.propertyName != 'opacity')
178           return;
179         if (e.target.classList.contains('transparent')) {
180           setIsVisible(e.target, false);
181         }
182       });
183
184       this.tracker.add(
185           this.getChildElement('.page > .close-button'),
186           'click',
187           this.onCloseClick_.bind(this));
188
189       this.tracker.add(
190           this.getChildElement('.sign-in'),
191           'click',
192           this.onSignInActivated_.bind(this));
193
194       this.tracker.add(
195           this.getChildElement('.cloudprint-promo > .close-button'),
196           'click',
197           this.onCloudprintPromoCloseButtonClick_.bind(this));
198       this.tracker.add(
199           this.searchBox_,
200           print_preview.SearchBox.EventType.SEARCH,
201           this.onSearch_.bind(this));
202       this.tracker.add(
203           this,
204           print_preview.DestinationListItem.EventType.SELECT,
205           this.onDestinationSelect_.bind(this));
206
207       this.tracker.add(
208           this.destinationStore_,
209           print_preview.DestinationStore.EventType.DESTINATIONS_INSERTED,
210           this.onDestinationsInserted_.bind(this));
211       this.tracker.add(
212           this.destinationStore_,
213           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
214           this.onDestinationStoreSelect_.bind(this));
215       this.tracker.add(
216           this.destinationStore_,
217           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_STARTED,
218           this.updateThrobbers_.bind(this));
219       this.tracker.add(
220           this.destinationStore_,
221           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
222           this.updateThrobbers_.bind(this));
223
224       this.tracker.add(
225           this.localList_,
226           print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED,
227           this.onManageLocalDestinationsActivated_.bind(this));
228       this.tracker.add(
229           this.cloudList_,
230           print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED,
231           this.onManageCloudDestinationsActivated_.bind(this));
232
233       this.tracker.add(
234           this.getElement(), 'click', this.onClick_.bind(this));
235       this.tracker.add(
236           this.getChildElement('.page'),
237           'webkitAnimationEnd',
238           this.onAnimationEnd_.bind(this));
239
240       this.tracker.add(
241           this.userInfo_,
242           print_preview.UserInfo.EventType.EMAIL_CHANGE,
243           this.onEmailChange_.bind(this));
244
245       this.tracker.add(window, 'resize', this.onWindowResize_.bind(this));
246
247       this.updateThrobbers_();
248
249       // Render any destinations already in the store.
250       this.renderDestinations_();
251     },
252
253     /** @override */
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">',
262           '</span>');
263     },
264
265     /**
266      * @return {number} Height available for destination lists, in pixels.
267      * @private
268      */
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;
276     },
277
278     /**
279      * Filters all destination lists with the given query.
280      * @param {?string} query Query to filter destination lists by.
281      * @private
282      */
283     filterLists_: function(query) {
284       this.recentList_.updateSearchQuery(query);
285       this.localList_.updateSearchQuery(query);
286       this.cloudList_.updateSearchQuery(query);
287     },
288
289     /**
290      * Resets the search query.
291      * @private
292      */
293     resetSearch_: function() {
294       this.searchBox_.setQuery(null);
295       this.filterLists_(null);
296     },
297
298     /**
299      * Renders all of the destinations in the destination store.
300      * @private
301      */
302     renderDestinations_: function() {
303       var recentDestinations = [];
304       var localDestinations = [];
305       var cloudDestinations = [];
306       var unregisteredCloudDestinations = [];
307
308       this.destinationStore_.destinations.forEach(function(destination) {
309         if (destination.isRecent) {
310           recentDestinations.push(destination);
311         }
312         if (destination.isLocal ||
313             destination.origin == print_preview.Destination.Origin.DEVICE) {
314           localDestinations.push(destination);
315         } else {
316           if (destination.connectionStatus ==
317                 print_preview.Destination.ConnectionStatus.UNREGISTERED) {
318             unregisteredCloudDestinations.push(destination);
319           } else {
320             cloudDestinations.push(destination);
321           }
322         }
323       });
324
325       var finalCloudDestinations = unregisteredCloudDestinations.slice(
326         0, DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_).concat(
327           cloudDestinations,
328           unregisteredCloudDestinations.slice(
329             DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_));
330
331       this.recentList_.updateDestinations(recentDestinations);
332       this.localList_.updateDestinations(localDestinations);
333       this.cloudList_.updateDestinations(finalCloudDestinations);
334     },
335
336     /**
337      * Reflows the destination lists according to the available height.
338      * @private
339      */
340     reflowLists_: function() {
341       if (!this.getIsVisible()) {
342         return;
343       }
344
345       var hasCloudList = getIsVisible(this.getChildElement('.cloud-list'));
346       var lists = [this.recentList_, this.localList_];
347       if (hasCloudList) {
348         lists.push(this.cloudList_);
349       }
350
351       var availableHeight = this.getAvailableListsHeight_();
352       this.getChildElement('.lists').style.maxHeight = availableHeight + 'px';
353
354       var maxListLength = lists.reduce(function(prevCount, list) {
355         return Math.max(prevCount, list.getDestinationsCount());
356       }, 0);
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_;
361         }, 0);
362         if (listsHeight > availableHeight) {
363           i -= 1;
364           break;
365         }
366       }
367
368       lists.forEach(function(list) {
369         list.updateShortListSize(i);
370       });
371
372       // Set height of the list manually so that search filter doesn't change
373       // lists height.
374       this.getChildElement('.lists').style.height =
375           lists.reduce(function(sum, list) {
376             return sum + list.getEstimatedHeightInPixels(i) +
377                 DestinationSearch.LIST_BOTTOM_PADDING_;
378           }, 0) + 'px';
379     },
380
381     /**
382      * Updates whether the throbbers for the various destination lists should be
383      * shown or hidden.
384      * @private
385      */
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);
394       this.reflowLists_();
395     },
396
397     /**
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.
401      * @private
402      */
403     onSearch_: function(evt) {
404       this.filterLists_(evt.query);
405     },
406
407     /**
408      * Called when the close button is clicked. Hides the search widget.
409      * @private
410      */
411     onCloseClick_: function() {
412       this.setIsVisible(false);
413       this.resetSearch_();
414       this.metrics_.incrementDestinationSearchBucket(
415           print_preview.Metrics.DestinationSearchBucket.CANCELED);
416     },
417
418     /**
419      * Called when a destination is selected. Clears the search and hides the
420      * widget.
421      * @param {Event} evt Contains the selected destination.
422      * @private
423      */
424     onDestinationSelect_: function(evt) {
425       this.setIsVisible(false);
426       this.resetSearch_();
427       this.destinationStore_.selectDestination(evt.destination);
428       this.metrics_.incrementDestinationSearchBucket(
429           print_preview.Metrics.DestinationSearchBucket.DESTINATION_SELECTED);
430     },
431
432     /**
433      * Called when a destination is selected. Selected destination are marked as
434      * recent, so we have to update our recent destinations list.
435      * @private
436      */
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);
443         }
444       });
445       this.recentList_.updateDestinations(recentDestinations);
446       this.reflowLists_();
447     },
448
449     /**
450      * Called when destinations are inserted into the store. Rerenders
451      * destinations.
452      * @private
453      */
454     onDestinationsInserted_: function() {
455       this.renderDestinations_();
456       this.reflowLists_();
457     },
458
459     /**
460      * Called when the manage cloud printers action is activated.
461      * @private
462      */
463     onManageCloudDestinationsActivated_: function() {
464       cr.dispatchSimpleEvent(
465           this,
466           print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS);
467     },
468
469     /**
470      * Called when the manage local printers action is activated.
471      * @private
472      */
473     onManageLocalDestinationsActivated_: function() {
474       cr.dispatchSimpleEvent(
475           this,
476           print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS);
477     },
478
479     /**
480      * Called when the "Sign in" link on the Google Cloud Print promo is
481      * activated.
482      * @private
483      */
484     onSignInActivated_: function() {
485       cr.dispatchSimpleEvent(this, DestinationSearch.EventType.SIGN_IN);
486       this.metrics_.incrementDestinationSearchBucket(
487           print_preview.Metrics.DestinationSearchBucket.SIGNIN_TRIGGERED);
488     },
489
490     /**
491      * Called when the close button on the cloud print promo is clicked. Hides
492      * the promo.
493      * @private
494      */
495     onCloudprintPromoCloseButtonClick_: function() {
496       setIsVisible(this.getChildElement('.cloudprint-promo'), false);
497     },
498
499     /**
500      * Called when the overlay is clicked. Pulses the page.
501      * @param {Event} event Contains the element that was clicked.
502      * @private
503      */
504     onClick_: function(event) {
505       if (event.target == this.getElement()) {
506         this.getChildElement('.page').classList.add('pulse');
507       }
508     },
509
510     /**
511      * Called when an animation ends on the page.
512      * @private
513      */
514     onAnimationEnd_: function() {
515       this.getChildElement('.page').classList.remove('pulse');
516     },
517
518     /**
519      * Called when the user's email field has changed. Updates the UI.
520      * @private
521      */
522     onEmailChange_: function() {
523       var userEmail = this.userInfo_.getUserEmail();
524       if (userEmail) {
525         this.setCloudPrintEmail(userEmail);
526       }
527     },
528
529     /**
530      * Called when the window is resized. Reflows layout of destination lists.
531      * @private
532      */
533     onWindowResize_: function() {
534       this.reflowLists_();
535     }
536   };
537
538   // Export
539   return {
540     DestinationSearch: DestinationSearch
541   };
542 });