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