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