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.
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.
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.
15 cr.define('local_discovery', function() {
19 * Prefix for printer management page URLs, relative to base cloud print URL.
22 var PRINTER_MANAGEMENT_PAGE_PREFIX = '#printers/';
24 // Histogram buckets for UMA tracking.
25 /** @const */ var DEVICES_PAGE_EVENTS = {
27 LOG_IN_STARTED_FROM_REGISTER_PROMO: 1,
28 LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO: 2,
29 ADD_PRINTER_CLICKED: 3,
31 REGISTER_CONFIRMED: 5,
36 REGISTER_CANCEL_ON_PRINTER: 10,
42 * Map of service names to corresponding service objects.
43 * @type {Object.<string,Service>}
48 * Whether or not the user is currently logged in.
51 var isUserLoggedIn = true;
54 * Whether or not the path-based dialog has been shown.
57 var dialogFromPathHasBeenShown = false;
60 * Focus manager for page.
62 var focusManager = null;
65 * Object that represents a device in the device list.
66 * @param {Object} info Information about the device.
69 function Device(info, registerEnabled) {
71 this.domElement = null;
72 this.registerButton = null;
73 this.registerEnabled = registerEnabled;
79 * @param {Object} info New information about the device.
81 updateDevice: function(info) {
89 removeDevice: function() {
90 this.deviceContainer().removeChild(this.domElement);
94 * Render the device to the device list.
96 renderDevice: function() {
97 if (this.domElement) {
98 clearElement(this.domElement);
100 this.domElement = document.createElement('div');
101 this.deviceContainer().appendChild(this.domElement);
104 this.registerButton = fillDeviceDescription(
106 this.info.human_readable_name,
107 this.info.description,
108 loadTimeData.getString('serviceRegister'),
109 this.showRegister.bind(this));
111 this.setRegisterEnabled(this.registerEnabled);
115 * Return the correct container for the device.
116 * @param {boolean} is_mine Whether or not the device is in the 'Registered'
119 deviceContainer: function() {
120 return $('register-device-list');
123 * Register the device.
125 register: function() {
126 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CONFIRMED);
127 chrome.send('registerDevice', [this.info.service_name]);
128 setRegisterPage('register-page-adding1');
131 * Show registrtation UI for device.
133 showRegister: function() {
134 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CLICKED);
135 $('register-message').textContent = loadTimeData.getStringF(
136 'registerConfirmMessage',
137 this.info.human_readable_name);
138 $('register-continue-button').onclick = this.register.bind(this);
139 showRegisterOverlay();
142 * Set registration button enabled/disabled
144 setRegisterEnabled: function(isEnabled) {
145 this.registerEnabled = isEnabled;
146 if (this.registerButton) {
147 this.registerButton.disabled = !isEnabled;
153 * Manages focus for local devices page.
155 * @extends {cr.ui.FocusManager}
157 function LocalDiscoveryFocusManager() {
158 cr.ui.FocusManager.call(this);
159 this.focusParent_ = document.body;
162 LocalDiscoveryFocusManager.prototype = {
163 __proto__: cr.ui.FocusManager.prototype,
165 getFocusParent: function() {
166 return document.querySelector('#overlay .showing') ||
172 * Returns a textual representation of the number of printers on the network.
173 * @return {string} Number of printers on the network as localized string.
175 function generateNumberPrintersAvailableText(numberPrinters) {
176 if (numberPrinters == 0) {
177 return loadTimeData.getString('printersOnNetworkZero');
178 } else if (numberPrinters == 1) {
179 return loadTimeData.getString('printersOnNetworkOne');
181 return loadTimeData.getStringF('printersOnNetworkMultiple',
187 * Fill device element with the description of a device.
188 * @param {HTMLElement} device_dom_element Element to be filled.
189 * @param {string} name Name of device.
190 * @param {string} description Description of device.
191 * @param {string} button_text Text to appear on button.
192 * @param {function()} button_action Action for button.
193 * @return {HTMLElement} The button (for enabling/disabling/rebinding)
195 function fillDeviceDescription(device_dom_element,
200 device_dom_element.classList.add('device');
201 device_dom_element.classList.add('printer');
203 var deviceInfo = document.createElement('div');
204 deviceInfo.className = 'device-info';
205 device_dom_element.appendChild(deviceInfo);
207 var deviceName = document.createElement('h3');
208 deviceName.className = 'device-name';
209 deviceName.textContent = name;
210 deviceInfo.appendChild(deviceName);
212 var deviceDescription = document.createElement('div');
213 deviceDescription.className = 'device-subline';
214 deviceDescription.textContent = description;
215 deviceInfo.appendChild(deviceDescription);
217 var button = document.createElement('button');
218 button.textContent = button_text;
219 button.addEventListener('click', button_action);
220 device_dom_element.appendChild(button);
226 * Show the register overlay.
228 function showRegisterOverlay() {
229 recordUmaEvent(DEVICES_PAGE_EVENTS.ADD_PRINTER_CLICKED);
231 var registerOverlay = $('register-overlay');
232 registerOverlay.classList.add('showing');
233 registerOverlay.focus();
235 $('overlay').hidden = false;
236 setRegisterPage('register-page-confirm');
240 * Hide the register overlay.
242 function hideRegisterOverlay() {
243 $('register-overlay').classList.remove('showing');
244 $('overlay').hidden = true;
248 * Clear a DOM element of all children.
249 * @param {HTMLElement} element DOM element to clear.
251 function clearElement(element) {
252 while (element.firstChild) {
253 element.removeChild(element.firstChild);
258 * Announce that a registration failed.
260 function onRegistrationFailed() {
261 $('error-message').textContent =
262 loadTimeData.getString('addingErrorMessage');
263 setRegisterPage('register-page-error');
264 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_FAILURE);
268 * Announce that a registration has been canceled on the printer.
270 function onRegistrationCanceledPrinter() {
271 $('error-message').textContent =
272 loadTimeData.getString('addingCanceledMessage');
273 setRegisterPage('register-page-error');
274 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CANCEL_ON_PRINTER);
278 * Announce that a registration has timed out.
280 function onRegistrationTimeout() {
281 $('error-message').textContent =
282 loadTimeData.getString('addingTimeoutMessage');
283 setRegisterPage('register-page-error');
284 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_TIMEOUT);
288 * Update UI to reflect that registration has been confirmed on the printer.
290 function onRegistrationConfirmedOnPrinter() {
291 setRegisterPage('register-page-adding2');
295 * Update device unregistered device list, and update related strings to
296 * reflect the number of devices available to register.
297 * @param {string} name Name of the device.
298 * @param {string} info Additional info of the device or null if the device
301 function onUnregisteredDeviceUpdate(name, info) {
303 if (devices.hasOwnProperty(name)) {
304 devices[name].updateDevice(info);
306 devices[name] = new Device(info, isUserLoggedIn);
307 devices[name].renderDevice();
310 if (name == getOverlayIDFromPath() && !dialogFromPathHasBeenShown) {
311 dialogFromPathHasBeenShown = true;
312 devices[name].showRegister();
315 if (devices.hasOwnProperty(name)) {
316 devices[name].removeDevice();
317 delete devices[name];
321 updateUIToReflectState();
325 * Create the DOM for a cloud device described by the device section.
326 * @param {Array.<Object>} devices_list List of devices.
328 function createCloudDeviceDOM(device) {
329 var devicesDomElement = document.createElement('div');
332 if (device.description == '') {
333 description = loadTimeData.getString('noDescription');
335 description = device.description;
338 fillDeviceDescription(devicesDomElement, device.display_name,
339 description, 'Manage' /*Localize*/,
340 manageCloudDevice.bind(null, device.id));
341 return devicesDomElement;
345 * Handle a list of cloud devices available to the user globally.
346 * @param {Array.<Object>} devices_list List of devices.
348 function onCloudDeviceListAvailable(devices_list) {
349 var devicesListLength = devices_list.length;
350 var devicesContainer = $('cloud-devices');
352 clearElement(devicesContainer);
353 $('cloud-devices-loading').hidden = true;
355 for (var i = 0; i < devicesListLength; i++) {
356 devicesContainer.appendChild(createCloudDeviceDOM(devices_list[i]));
361 * Handle the case where the list of cloud devices is not available.
363 function onCloudDeviceListUnavailable() {
364 if (isUserLoggedIn) {
365 $('cloud-devices-loading').hidden = true;
366 $('cloud-devices-unavailable').hidden = false;
371 * Handle the case where the cache for local devices has been flushed..
373 function onDeviceCacheFlushed() {
374 for (var deviceName in devices) {
375 devices[deviceName].removeDevice();
376 delete devices[deviceName];
379 updateUIToReflectState();
383 * Update UI strings to reflect the number of local devices.
385 function updateUIToReflectState() {
386 var numberPrinters = $('register-device-list').children.length;
387 if (numberPrinters == 0) {
388 $('no-printers-message').hidden = false;
390 $('register-login-promo').hidden = true;
392 $('no-printers-message').hidden = true;
393 $('register-login-promo').hidden = isUserLoggedIn;
398 * Announce that a registration succeeeded.
400 function onRegistrationSuccess(device_data) {
401 hideRegisterOverlay();
403 if (device_data.service_name == getOverlayIDFromPath()) {
407 var deviceDOM = createCloudDeviceDOM(device_data);
408 $('cloud-devices').insertBefore(deviceDOM, $('cloud-devices').firstChild);
409 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_SUCCESS);
413 * Update visibility status for page.
415 function updateVisibility() {
416 chrome.send('isVisible', [!document.hidden]);
420 * Set the page that the register wizard is on.
421 * @param {string} page_id ID string for page.
423 function setRegisterPage(page_id) {
424 var pages = $('register-overlay').querySelectorAll('.register-page');
425 var pagesLength = pages.length;
426 for (var i = 0; i < pagesLength; i++) {
427 pages[i].hidden = true;
430 $(page_id).hidden = false;
434 * Request the printer list.
436 function requestPrinterList() {
437 if (isUserLoggedIn) {
438 clearElement($('cloud-devices'));
439 $('cloud-devices-loading').hidden = false;
440 $('cloud-devices-unavailable').hidden = true;
442 chrome.send('requestPrinterList');
447 * Go to management page for a cloud device.
448 * @param {string} device_id ID of device.
450 function manageCloudDevice(device_id) {
451 recordUmaEvent(DEVICES_PAGE_EVENTS.MANAGE_CLICKED);
452 chrome.send('openCloudPrintURL',
453 [PRINTER_MANAGEMENT_PAGE_PREFIX + device_id]);
457 * Record an event in the UMA histogram.
458 * @param {number} eventId The id of the event to be recorded.
461 function recordUmaEvent(eventId) {
462 chrome.send('metricsHandler:recordInHistogram',
463 ['LocalDiscovery.DevicesPage', eventId, DEVICES_PAGE_EVENTS.MAX_EVENT]);
467 * Cancel the registration.
469 function cancelRegistration() {
470 hideRegisterOverlay();
471 chrome.send('cancelRegistration');
472 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CANCEL);
476 * Retry loading the devices from Google Cloud Print.
478 function retryLoadCloudDevices() {
479 requestPrinterList();
483 * User is not logged in.
485 function setUserLoggedIn(userLoggedIn) {
486 isUserLoggedIn = userLoggedIn;
488 $('cloud-devices-login-promo').hidden = isUserLoggedIn;
491 if (isUserLoggedIn) {
492 requestPrinterList();
493 $('register-login-promo').hidden = true;
495 $('cloud-devices-loading').hidden = true;
496 $('cloud-devices-unavailable').hidden = true;
497 clearElement($('cloud-devices'));
498 hideRegisterOverlay();
501 updateUIToReflectState();
503 for (var device in devices) {
504 devices[device].setRegisterEnabled(isUserLoggedIn);
508 function openSignInPage() {
509 chrome.send('showSyncUI');
512 function registerLoginButtonClicked() {
513 recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_PROMO);
517 function cloudDevicesLoginButtonClicked() {
518 recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO);
523 * Set the Cloud Print proxy UI to enabled, disabled, or processing.
526 function setupCloudPrintConnectorSection(disabled, label, allowed) {
527 if (!cr.isChromeOS && !cr.isMac) {
528 $('cloudPrintConnectorLabel').textContent = label;
529 if (disabled || !allowed) {
530 $('cloudPrintConnectorSetupButton').textContent =
531 loadTimeData.getString('cloudPrintConnectorDisabledButton');
533 $('cloudPrintConnectorSetupButton').textContent =
534 loadTimeData.getString('cloudPrintConnectorEnabledButton');
536 $('cloudPrintConnectorSetupButton').disabled = !allowed;
539 $('cloudPrintConnectorSetupButton').onclick = function(event) {
540 // Disable the button, set its text to the intermediate state.
541 $('cloudPrintConnectorSetupButton').textContent =
542 loadTimeData.getString('cloudPrintConnectorEnablingButton');
543 $('cloudPrintConnectorSetupButton').disabled = true;
544 chrome.send('showCloudPrintSetupDialog');
547 $('cloudPrintConnectorSetupButton').onclick = function(event) {
548 chrome.send('disableCloudPrintConnector');
549 requestPrinterList();
555 function removeCloudPrintConnectorSection() {
556 if (!cr.isChromeOS && !cr.isMac) {
557 var connectorSectionElm = $('cloud-print-connector-section');
558 if (connectorSectionElm)
559 connectorSectionElm.parentNode.removeChild(connectorSectionElm);
563 function getOverlayIDFromPath() {
564 if (document.location.pathname == '/register') {
565 var params = parseQueryParams(document.location);
566 return params['id'] || null;
570 document.addEventListener('DOMContentLoaded', function() {
571 cr.ui.overlay.setupOverlay($('overlay'));
572 cr.ui.overlay.globalInitialization();
573 $('overlay').addEventListener('cancelOverlay', cancelRegistration);
575 var cancelButtons = document.querySelectorAll('.register-cancel');
576 var cancelButtonsLength = cancelButtons.length;
577 for (var i = 0; i < cancelButtonsLength; i++) {
578 cancelButtons[i].addEventListener('click', cancelRegistration);
581 $('register-error-exit').addEventListener('click', cancelRegistration);
584 $('cloud-devices-retry-button').addEventListener('click',
585 retryLoadCloudDevices);
587 $('cloud-devices-login-button').addEventListener(
589 cloudDevicesLoginButtonClicked);
591 $('register-login-button').addEventListener(
593 registerLoginButtonClicked);
596 document.addEventListener('visibilitychange', updateVisibility, false);
598 focusManager = new LocalDiscoveryFocusManager();
599 focusManager.initialize();
601 chrome.send('start');
602 recordUmaEvent(DEVICES_PAGE_EVENTS.OPENED);
606 onRegistrationSuccess: onRegistrationSuccess,
607 onRegistrationFailed: onRegistrationFailed,
608 onUnregisteredDeviceUpdate: onUnregisteredDeviceUpdate,
609 onRegistrationConfirmedOnPrinter: onRegistrationConfirmedOnPrinter,
610 onCloudDeviceListAvailable: onCloudDeviceListAvailable,
611 onCloudDeviceListUnavailable: onCloudDeviceListUnavailable,
612 onDeviceCacheFlushed: onDeviceCacheFlushed,
613 onRegistrationCanceledPrinter: onRegistrationCanceledPrinter,
614 onRegistrationTimeout: onRegistrationTimeout,
615 setUserLoggedIn: setUserLoggedIn,
616 setupCloudPrintConnectorSection: setupCloudPrintConnectorSection,
617 removeCloudPrintConnectorSection: removeCloudPrintConnectorSection