Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / local_discovery / local_discovery.js
1 // Copyright 2013 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 /**
6  * Javascript for local_discovery.html, served from chrome://devices/
7  * This is used to show discoverable devices near the user as well as
8  * cloud devices registered to them.
9  *
10  * The object defined in this javascript file listens for callbacks from the
11  * C++ code saying that a new device is available as well as manages the UI for
12  * registering a device on the local network.
13  */
14
15 cr.define('local_discovery', function() {
16   'use strict';
17
18   /**
19    * Prefix for printer management page URLs, relative to base cloud print URL.
20    * @type {string}
21    */
22   var PRINTER_MANAGEMENT_PAGE_PREFIX = '#printers/';
23
24   // Histogram buckets for UMA tracking.
25   /** @const */ var DEVICES_PAGE_EVENTS = {
26     OPENED: 0,
27     LOG_IN_STARTED_FROM_REGISTER_PROMO: 1,
28     LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO: 2,
29     ADD_PRINTER_CLICKED: 3,
30     REGISTER_CLICKED: 4,
31     REGISTER_CONFIRMED: 5,
32     REGISTER_SUCCESS: 6,
33     REGISTER_CANCEL: 7,
34     REGISTER_FAILURE: 8,
35     MANAGE_CLICKED: 9,
36     REGISTER_CANCEL_ON_PRINTER: 10,
37     REGISTER_TIMEOUT: 11,
38     LOG_IN_STARTED_FROM_REGISTER_OVERLAY_PROMO: 12,
39     MAX_EVENT: 13,
40   };
41
42   /**
43    * Map of service names to corresponding service objects.
44    * @type {Object.<string,Service>}
45    */
46   var devices = {};
47
48   /**
49    * Whether or not the user is currently logged in.
50    * @type bool
51    */
52   var isUserLoggedIn = true;
53
54   /**
55    * Whether or not the path-based dialog has been shown.
56    * @type bool
57    */
58   var dialogFromPathHasBeenShown = false;
59
60   /**
61    * Focus manager for page.
62    */
63   var focusManager = null;
64
65   /**
66    * Object that represents a device in the device list.
67    * @param {Object} info Information about the device.
68    * @constructor
69    */
70   function Device(info, registerEnabled) {
71     this.info = info;
72     this.domElement = null;
73     this.registerButton = null;
74     this.registerEnabled = registerEnabled;
75   }
76
77   Device.prototype = {
78     /**
79      * Update the device.
80      * @param {Object} info New information about the device.
81      */
82     updateDevice: function(info) {
83       this.info = info;
84       this.renderDevice();
85     },
86
87     /**
88      * Delete the device.
89      */
90     removeDevice: function() {
91       this.deviceContainer().removeChild(this.domElement);
92     },
93
94     /**
95      * Render the device to the device list.
96      */
97     renderDevice: function() {
98       if (this.domElement) {
99         clearElement(this.domElement);
100       } else {
101         this.domElement = document.createElement('div');
102         this.deviceContainer().appendChild(this.domElement);
103       }
104
105       this.registerButton = fillDeviceDescription(
106         this.domElement,
107         this.info.human_readable_name,
108         this.info.description,
109         loadTimeData.getString('serviceRegister'),
110         this.showRegister.bind(this));
111
112       this.setRegisterEnabled(this.registerEnabled);
113     },
114
115     /**
116      * Return the correct container for the device.
117      * @param {boolean} is_mine Whether or not the device is in the 'Registered'
118      *    section.
119      */
120     deviceContainer: function() {
121       return $('register-device-list');
122     },
123     /**
124      * Register the device.
125      */
126     register: function() {
127       recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CONFIRMED);
128       chrome.send('registerDevice', [this.info.service_name]);
129       setRegisterPage('register-page-adding1');
130     },
131     /**
132      * Show registrtation UI for device.
133      */
134     showRegister: function() {
135       recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CLICKED);
136       $('register-message').textContent = loadTimeData.getStringF(
137         'registerConfirmMessage',
138         this.info.human_readable_name);
139       $('register-continue-button').onclick = this.register.bind(this);
140       showRegisterOverlay();
141     },
142     /**
143      * Set registration button enabled/disabled
144      */
145     setRegisterEnabled: function(isEnabled) {
146       this.registerEnabled = isEnabled;
147       if (this.registerButton) {
148         this.registerButton.disabled = !isEnabled;
149       }
150     }
151   };
152
153   /**
154    * Manages focus for local devices page.
155    * @constructor
156    * @extends {cr.ui.FocusManager}
157    */
158   function LocalDiscoveryFocusManager() {
159     cr.ui.FocusManager.call(this);
160     this.focusParent_ = document.body;
161   }
162
163   LocalDiscoveryFocusManager.prototype = {
164     __proto__: cr.ui.FocusManager.prototype,
165     /** @override */
166     getFocusParent: function() {
167       return document.querySelector('#overlay .showing') ||
168         $('main-page');
169     }
170   };
171
172   /**
173    * Returns a textual representation of the number of printers on the network.
174    * @return {string} Number of printers on the network as localized string.
175    */
176   function generateNumberPrintersAvailableText(numberPrinters) {
177     if (numberPrinters == 0) {
178       return loadTimeData.getString('printersOnNetworkZero');
179     } else if (numberPrinters == 1) {
180       return loadTimeData.getString('printersOnNetworkOne');
181     } else {
182       return loadTimeData.getStringF('printersOnNetworkMultiple',
183                                      numberPrinters);
184     }
185   }
186
187   /**
188    * Fill device element with the description of a device.
189    * @param {HTMLElement} device_dom_element Element to be filled.
190    * @param {string} name Name of device.
191    * @param {string} description Description of device.
192    * @param {string} button_text Text to appear on button.
193    * @param {function()} button_action Action for button.
194    * @return {HTMLElement} The button (for enabling/disabling/rebinding)
195    */
196   function fillDeviceDescription(device_dom_element,
197                                 name,
198                                 description,
199                                 button_text,
200                                 button_action) {
201     device_dom_element.classList.add('device');
202     device_dom_element.classList.add('printer');
203
204     var deviceInfo = document.createElement('div');
205     deviceInfo.className = 'device-info';
206     device_dom_element.appendChild(deviceInfo);
207
208     var deviceName = document.createElement('h3');
209     deviceName.className = 'device-name';
210     deviceName.textContent = name;
211     deviceInfo.appendChild(deviceName);
212
213     var deviceDescription = document.createElement('div');
214     deviceDescription.className = 'device-subline';
215     deviceDescription.textContent = description;
216     deviceInfo.appendChild(deviceDescription);
217
218     var button = document.createElement('button');
219     button.textContent = button_text;
220     button.addEventListener('click', button_action);
221     device_dom_element.appendChild(button);
222
223     return button;
224   }
225
226   /**
227    * Show the register overlay.
228    */
229   function showRegisterOverlay() {
230     recordUmaEvent(DEVICES_PAGE_EVENTS.ADD_PRINTER_CLICKED);
231
232     var registerOverlay = $('register-overlay');
233     registerOverlay.classList.add('showing');
234     registerOverlay.focus();
235
236     $('overlay').hidden = false;
237     setRegisterPage('register-page-confirm');
238   }
239
240   /**
241    * Hide the register overlay.
242    */
243   function hideRegisterOverlay() {
244     $('register-overlay').classList.remove('showing');
245     $('overlay').hidden = true;
246   }
247
248   /**
249    * Clear a DOM element of all children.
250    * @param {HTMLElement} element DOM element to clear.
251    */
252   function clearElement(element) {
253     while (element.firstChild) {
254       element.removeChild(element.firstChild);
255     }
256   }
257
258   /**
259    * Announce that a registration failed.
260    */
261   function onRegistrationFailed() {
262     $('error-message').textContent =
263       loadTimeData.getString('addingErrorMessage');
264     setRegisterPage('register-page-error');
265     recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_FAILURE);
266   }
267
268   /**
269    * Announce that a registration has been canceled on the printer.
270    */
271   function onRegistrationCanceledPrinter() {
272     $('error-message').textContent =
273       loadTimeData.getString('addingCanceledMessage');
274     setRegisterPage('register-page-error');
275     recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CANCEL_ON_PRINTER);
276   }
277
278   /**
279    * Announce that a registration has timed out.
280    */
281   function onRegistrationTimeout() {
282     $('error-message').textContent =
283       loadTimeData.getString('addingTimeoutMessage');
284     setRegisterPage('register-page-error');
285     recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_TIMEOUT);
286   }
287
288   /**
289    * Update UI to reflect that registration has been confirmed on the printer.
290    */
291   function onRegistrationConfirmedOnPrinter() {
292     setRegisterPage('register-page-adding2');
293   }
294
295   /**
296    * Update device unregistered device list, and update related strings to
297    * reflect the number of devices available to register.
298    * @param {string} name Name of the device.
299    * @param {string} info Additional info of the device or null if the device
300    *                          has been removed.
301    */
302   function onUnregisteredDeviceUpdate(name, info) {
303     if (info) {
304       if (devices.hasOwnProperty(name)) {
305         devices[name].updateDevice(info);
306       } else {
307         devices[name] = new Device(info, isUserLoggedIn);
308         devices[name].renderDevice();
309       }
310
311       if (name == getOverlayIDFromPath() && !dialogFromPathHasBeenShown) {
312         dialogFromPathHasBeenShown = true;
313         devices[name].showRegister();
314       }
315     } else {
316       if (devices.hasOwnProperty(name)) {
317         devices[name].removeDevice();
318         delete devices[name];
319       }
320     }
321
322     updateUIToReflectState();
323   }
324
325   /**
326    * Create the DOM for a cloud device described by the device section.
327    * @param {Array.<Object>} devices_list List of devices.
328    */
329   function createCloudDeviceDOM(device) {
330     var devicesDomElement = document.createElement('div');
331
332     var description;
333     if (device.description == '') {
334         description = loadTimeData.getString('noDescription');
335       } else {
336         description = device.description;
337       }
338
339     fillDeviceDescription(devicesDomElement, device.display_name,
340                           description, 'Manage' /*Localize*/,
341                           manageCloudDevice.bind(null, device.id));
342     return devicesDomElement;
343   }
344
345   /**
346    * Handle a list of cloud devices available to the user globally.
347    * @param {Array.<Object>} devices_list List of devices.
348    */
349   function onCloudDeviceListAvailable(devices_list) {
350     var devicesListLength = devices_list.length;
351     var devicesContainer = $('cloud-devices');
352
353     clearElement(devicesContainer);
354     $('cloud-devices-loading').hidden = true;
355
356     for (var i = 0; i < devicesListLength; i++) {
357       devicesContainer.appendChild(createCloudDeviceDOM(devices_list[i]));
358     }
359   }
360
361   /**
362    * Handle the case where the list of cloud devices is not available.
363    */
364   function onCloudDeviceListUnavailable() {
365     if (isUserLoggedIn) {
366       $('cloud-devices-loading').hidden = true;
367       $('cloud-devices-unavailable').hidden = false;
368     }
369   }
370
371   /**
372    * Handle the case where the cache for local devices has been flushed..
373    */
374   function onDeviceCacheFlushed() {
375     for (var deviceName in devices) {
376       devices[deviceName].removeDevice();
377       delete devices[deviceName];
378     }
379
380     updateUIToReflectState();
381   }
382
383   /**
384    * Update UI strings to reflect the number of local devices.
385    */
386   function updateUIToReflectState() {
387     var numberPrinters = $('register-device-list').children.length;
388     if (numberPrinters == 0) {
389       $('no-printers-message').hidden = false;
390
391       $('register-login-promo').hidden = true;
392     } else {
393       $('no-printers-message').hidden = true;
394       $('register-login-promo').hidden = isUserLoggedIn;
395     }
396   }
397
398   /**
399    * Announce that a registration succeeeded.
400    */
401   function onRegistrationSuccess(device_data) {
402     hideRegisterOverlay();
403
404     if (device_data.service_name == getOverlayIDFromPath()) {
405       window.close();
406     }
407
408     var deviceDOM = createCloudDeviceDOM(device_data);
409     $('cloud-devices').insertBefore(deviceDOM, $('cloud-devices').firstChild);
410     recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_SUCCESS);
411   }
412
413   /**
414    * Update visibility status for page.
415    */
416   function updateVisibility() {
417     chrome.send('isVisible', [!document.hidden]);
418   }
419
420   /**
421    * Set the page that the register wizard is on.
422    * @param {string} page_id ID string for page.
423    */
424   function setRegisterPage(page_id) {
425     var pages = $('register-overlay').querySelectorAll('.register-page');
426     var pagesLength = pages.length;
427     for (var i = 0; i < pagesLength; i++) {
428       pages[i].hidden = true;
429     }
430
431     $(page_id).hidden = false;
432   }
433
434   /**
435    * Request the printer list.
436    */
437   function requestPrinterList() {
438     if (isUserLoggedIn) {
439       clearElement($('cloud-devices'));
440       $('cloud-devices-loading').hidden = false;
441       $('cloud-devices-unavailable').hidden = true;
442
443       chrome.send('requestPrinterList');
444     }
445   }
446
447   /**
448    * Go to management page for a cloud device.
449    * @param {string} device_id ID of device.
450    */
451   function manageCloudDevice(device_id) {
452     recordUmaEvent(DEVICES_PAGE_EVENTS.MANAGE_CLICKED);
453     chrome.send('openCloudPrintURL',
454                 [PRINTER_MANAGEMENT_PAGE_PREFIX + device_id]);
455   }
456
457   /**
458   * Record an event in the UMA histogram.
459   * @param {number} eventId The id of the event to be recorded.
460   * @private
461   */
462   function recordUmaEvent(eventId) {
463     chrome.send('metricsHandler:recordInHistogram',
464       ['LocalDiscovery.DevicesPage', eventId, DEVICES_PAGE_EVENTS.MAX_EVENT]);
465   }
466
467   /**
468    * Cancel the registration.
469    */
470   function cancelRegistration() {
471     hideRegisterOverlay();
472     chrome.send('cancelRegistration');
473     recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CANCEL);
474   }
475
476   /**
477    * Retry loading the devices from Google Cloud Print.
478    */
479   function retryLoadCloudDevices() {
480     requestPrinterList();
481   }
482
483   /**
484    * User is not logged in.
485    */
486   function setUserLoggedIn(userLoggedIn) {
487     isUserLoggedIn = userLoggedIn;
488
489     $('cloud-devices-login-promo').hidden = isUserLoggedIn;
490     $('register-overlay-login-promo').hidden = isUserLoggedIn;
491     $('register-continue-button').disabled = !isUserLoggedIn;
492
493     if (isUserLoggedIn) {
494       requestPrinterList();
495       $('register-login-promo').hidden = true;
496     } else {
497       $('cloud-devices-loading').hidden = true;
498       $('cloud-devices-unavailable').hidden = true;
499       clearElement($('cloud-devices'));
500       hideRegisterOverlay();
501     }
502
503     updateUIToReflectState();
504
505     for (var device in devices) {
506       devices[device].setRegisterEnabled(isUserLoggedIn);
507     }
508   }
509
510   function openSignInPage() {
511     chrome.send('showSyncUI');
512   }
513
514   function registerLoginButtonClicked() {
515     recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_PROMO);
516     openSignInPage();
517   }
518
519   function registerOverlayLoginButtonClicked() {
520     recordUmaEvent(
521       DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_OVERLAY_PROMO);
522     openSignInPage();
523   }
524
525   function cloudDevicesLoginButtonClicked() {
526     recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO);
527     openSignInPage();
528   }
529
530   /**
531    * Set the Cloud Print proxy UI to enabled, disabled, or processing.
532    * @private
533    */
534   function setupCloudPrintConnectorSection(disabled, label, allowed) {
535     if (!cr.isChromeOS && !cr.isMac) {
536       $('cloudPrintConnectorLabel').textContent = label;
537       if (disabled || !allowed) {
538         $('cloudPrintConnectorSetupButton').textContent =
539           loadTimeData.getString('cloudPrintConnectorDisabledButton');
540       } else {
541         $('cloudPrintConnectorSetupButton').textContent =
542           loadTimeData.getString('cloudPrintConnectorEnabledButton');
543       }
544       $('cloudPrintConnectorSetupButton').disabled = !allowed;
545
546       if (disabled) {
547         $('cloudPrintConnectorSetupButton').onclick = function(event) {
548           // Disable the button, set its text to the intermediate state.
549           $('cloudPrintConnectorSetupButton').textContent =
550             loadTimeData.getString('cloudPrintConnectorEnablingButton');
551           $('cloudPrintConnectorSetupButton').disabled = true;
552           chrome.send('showCloudPrintSetupDialog');
553         };
554       } else {
555         $('cloudPrintConnectorSetupButton').onclick = function(event) {
556           chrome.send('disableCloudPrintConnector');
557           requestPrinterList();
558         };
559       }
560     }
561   }
562
563   function removeCloudPrintConnectorSection() {
564     if (!cr.isChromeOS && !cr.isMac) {
565        var connectorSectionElm = $('cloud-print-connector-section');
566        if (connectorSectionElm)
567           connectorSectionElm.parentNode.removeChild(connectorSectionElm);
568      }
569   }
570
571   function getOverlayIDFromPath() {
572     if (document.location.pathname == '/register') {
573       var params = parseQueryParams(document.location);
574       return params['id'] || null;
575     }
576   }
577
578   document.addEventListener('DOMContentLoaded', function() {
579     cr.ui.overlay.setupOverlay($('overlay'));
580     cr.ui.overlay.globalInitialization();
581     $('overlay').addEventListener('cancelOverlay', cancelRegistration);
582
583     var cancelButtons = document.querySelectorAll('.register-cancel');
584     var cancelButtonsLength = cancelButtons.length;
585     for (var i = 0; i < cancelButtonsLength; i++) {
586       cancelButtons[i].addEventListener('click', cancelRegistration);
587     }
588
589     $('register-error-exit').addEventListener('click', cancelRegistration);
590
591
592     $('cloud-devices-retry-button').addEventListener('click',
593                                                      retryLoadCloudDevices);
594
595     $('cloud-devices-login-button').addEventListener(
596       'click',
597       cloudDevicesLoginButtonClicked);
598
599     $('register-login-button').addEventListener(
600       'click',
601       registerLoginButtonClicked);
602
603     $('register-overlay-login-button').addEventListener(
604       'click',
605       registerOverlayLoginButtonClicked);
606
607     updateVisibility();
608     document.addEventListener('visibilitychange', updateVisibility, false);
609
610     focusManager = new LocalDiscoveryFocusManager();
611     focusManager.initialize();
612
613     chrome.send('start');
614     recordUmaEvent(DEVICES_PAGE_EVENTS.OPENED);
615   });
616
617   return {
618     onRegistrationSuccess: onRegistrationSuccess,
619     onRegistrationFailed: onRegistrationFailed,
620     onUnregisteredDeviceUpdate: onUnregisteredDeviceUpdate,
621     onRegistrationConfirmedOnPrinter: onRegistrationConfirmedOnPrinter,
622     onCloudDeviceListAvailable: onCloudDeviceListAvailable,
623     onCloudDeviceListUnavailable: onCloudDeviceListUnavailable,
624     onDeviceCacheFlushed: onDeviceCacheFlushed,
625     onRegistrationCanceledPrinter: onRegistrationCanceledPrinter,
626     onRegistrationTimeout: onRegistrationTimeout,
627     setUserLoggedIn: setUserLoggedIn,
628     setupCloudPrintConnectorSection: setupCloudPrintConnectorSection,
629     removeCloudPrintConnectorSection: removeCloudPrintConnectorSection
630   };
631 });