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() {
18 // Histogram buckets for UMA tracking.
19 /** @const */ var DEVICES_PAGE_EVENTS = {
21 LOG_IN_STARTED_FROM_REGISTER_PROMO: 1,
22 LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO: 2,
23 ADD_PRINTER_CLICKED: 3,
25 REGISTER_CONFIRMED: 5,
30 REGISTER_CANCEL_ON_PRINTER: 10,
32 LOG_IN_STARTED_FROM_REGISTER_OVERLAY_PROMO: 12,
37 * Map of service names to corresponding service objects.
38 * @type {Object.<string,Service>}
43 * Whether or not the user is currently logged in.
46 var isUserLoggedIn = true;
49 * Whether or not the path-based dialog has been shown.
52 var dialogFromPathHasBeenShown = false;
55 * Focus manager for page.
57 var focusManager = null;
60 * Object that represents a device in the device list.
61 * @param {Object} info Information about the device.
64 function Device(info, registerEnabled) {
66 this.domElement = null;
67 this.registerButton = null;
68 this.registerEnabled = registerEnabled;
74 * @param {Object} info New information about the device.
76 updateDevice: function(info) {
84 removeDevice: function() {
85 this.deviceContainer().removeChild(this.domElement);
89 * Render the device to the device list.
91 renderDevice: function() {
92 if (this.domElement) {
93 clearElement(this.domElement);
95 this.domElement = document.createElement('div');
96 this.deviceContainer().appendChild(this.domElement);
99 this.registerButton = fillDeviceDescription(
101 this.info.human_readable_name,
102 this.info.description,
103 loadTimeData.getString('serviceRegister'),
104 this.showRegister.bind(this));
106 this.setRegisterEnabled(this.registerEnabled);
110 * Return the correct container for the device.
111 * @param {boolean} is_mine Whether or not the device is in the 'Registered'
114 deviceContainer: function() {
115 return $('register-device-list');
118 * Register the device.
120 register: function() {
121 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CONFIRMED);
122 chrome.send('registerDevice', [this.info.service_name]);
123 setRegisterPage('register-page-adding1');
126 * Show registrtation UI for device.
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();
137 * Set registration button enabled/disabled
139 setRegisterEnabled: function(isEnabled) {
140 this.registerEnabled = isEnabled;
141 if (this.registerButton) {
142 this.registerButton.disabled = !isEnabled;
148 * Manages focus for local devices page.
150 * @extends {cr.ui.FocusManager}
152 function LocalDiscoveryFocusManager() {
153 cr.ui.FocusManager.call(this);
154 this.focusParent_ = document.body;
157 LocalDiscoveryFocusManager.prototype = {
158 __proto__: cr.ui.FocusManager.prototype,
160 getFocusParent: function() {
161 return document.querySelector('#overlay .showing') ||
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.
170 function generateNumberPrintersAvailableText(numberPrinters) {
171 if (numberPrinters == 0) {
172 return loadTimeData.getString('printersOnNetworkZero');
173 } else if (numberPrinters == 1) {
174 return loadTimeData.getString('printersOnNetworkOne');
176 return loadTimeData.getStringF('printersOnNetworkMultiple',
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)
190 function fillDeviceDescription(device_dom_element,
195 device_dom_element.classList.add('device');
196 device_dom_element.classList.add('printer');
198 var deviceInfo = document.createElement('div');
199 deviceInfo.className = 'device-info';
200 device_dom_element.appendChild(deviceInfo);
202 var deviceName = document.createElement('h3');
203 deviceName.className = 'device-name';
204 deviceName.textContent = name;
205 deviceInfo.appendChild(deviceName);
207 var deviceDescription = document.createElement('div');
208 deviceDescription.className = 'device-subline';
209 deviceDescription.textContent = description;
210 deviceInfo.appendChild(deviceDescription);
212 var button = document.createElement('button');
213 button.textContent = button_text;
214 button.addEventListener('click', button_action);
215 device_dom_element.appendChild(button);
221 * Show the register overlay.
223 function showRegisterOverlay() {
224 recordUmaEvent(DEVICES_PAGE_EVENTS.ADD_PRINTER_CLICKED);
226 var registerOverlay = $('register-overlay');
227 registerOverlay.classList.add('showing');
228 registerOverlay.focus();
230 $('overlay').hidden = false;
231 setRegisterPage('register-page-confirm');
235 * Hide the register overlay.
237 function hideRegisterOverlay() {
238 $('register-overlay').classList.remove('showing');
239 $('overlay').hidden = true;
243 * Clear a DOM element of all children.
244 * @param {HTMLElement} element DOM element to clear.
246 function clearElement(element) {
247 while (element.firstChild) {
248 element.removeChild(element.firstChild);
253 * Announce that a registration failed.
255 function onRegistrationFailed() {
256 $('error-message').textContent =
257 loadTimeData.getString('addingErrorMessage');
258 setRegisterPage('register-page-error');
259 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_FAILURE);
263 * Announce that a registration has been canceled on the printer.
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);
273 * Announce that a registration has timed out.
275 function onRegistrationTimeout() {
276 $('error-message').textContent =
277 loadTimeData.getString('addingTimeoutMessage');
278 setRegisterPage('register-page-error');
279 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_TIMEOUT);
283 * Update UI to reflect that registration has been confirmed on the printer.
285 function onRegistrationConfirmedOnPrinter() {
286 setRegisterPage('register-page-adding2');
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
296 function onUnregisteredDeviceUpdate(name, info) {
298 if (devices.hasOwnProperty(name)) {
299 devices[name].updateDevice(info);
301 devices[name] = new Device(info, isUserLoggedIn);
302 devices[name].renderDevice();
305 if (name == getOverlayIDFromPath() && !dialogFromPathHasBeenShown) {
306 dialogFromPathHasBeenShown = true;
307 devices[name].showRegister();
310 if (devices.hasOwnProperty(name)) {
311 devices[name].removeDevice();
312 delete devices[name];
316 updateUIToReflectState();
320 * Create the DOM for a cloud device described by the device section.
321 * @param {Array.<Object>} devices_list List of devices.
323 function createCloudDeviceDOM(device) {
324 var devicesDomElement = document.createElement('div');
327 if (device.description == '') {
328 description = loadTimeData.getString('noDescription');
330 description = device.description;
333 fillDeviceDescription(devicesDomElement, device.display_name,
334 description, 'Manage' /*Localize*/,
335 manageCloudDevice.bind(null, device.id));
336 return devicesDomElement;
340 * Handle a list of cloud devices available to the user globally.
341 * @param {Array.<Object>} devices_list List of devices.
343 function onCloudDeviceListAvailable(devices_list) {
344 var devicesListLength = devices_list.length;
345 var devicesContainer = $('cloud-devices');
347 clearElement(devicesContainer);
348 $('cloud-devices-loading').hidden = true;
350 for (var i = 0; i < devicesListLength; i++) {
351 devicesContainer.appendChild(createCloudDeviceDOM(devices_list[i]));
356 * Handle the case where the list of cloud devices is not available.
358 function onCloudDeviceListUnavailable() {
359 if (isUserLoggedIn) {
360 $('cloud-devices-loading').hidden = true;
361 $('cloud-devices-unavailable').hidden = false;
366 * Handle the case where the cache for local devices has been flushed..
368 function onDeviceCacheFlushed() {
369 for (var deviceName in devices) {
370 devices[deviceName].removeDevice();
371 delete devices[deviceName];
374 updateUIToReflectState();
378 * Update UI strings to reflect the number of local devices.
380 function updateUIToReflectState() {
381 var numberPrinters = $('register-device-list').children.length;
382 if (numberPrinters == 0) {
383 $('no-printers-message').hidden = false;
385 $('register-login-promo').hidden = true;
387 $('no-printers-message').hidden = true;
388 $('register-login-promo').hidden = isUserLoggedIn;
393 * Announce that a registration succeeeded.
395 function onRegistrationSuccess(device_data) {
396 hideRegisterOverlay();
398 if (device_data.service_name == getOverlayIDFromPath()) {
402 var deviceDOM = createCloudDeviceDOM(device_data);
403 $('cloud-devices').insertBefore(deviceDOM, $('cloud-devices').firstChild);
404 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_SUCCESS);
408 * Update visibility status for page.
410 function updateVisibility() {
411 chrome.send('isVisible', [!document.hidden]);
415 * Set the page that the register wizard is on.
416 * @param {string} page_id ID string for page.
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;
425 $(page_id).hidden = false;
429 * Request the printer list.
431 function requestPrinterList() {
432 if (isUserLoggedIn) {
433 clearElement($('cloud-devices'));
434 $('cloud-devices-loading').hidden = false;
435 $('cloud-devices-unavailable').hidden = true;
437 chrome.send('requestPrinterList');
442 * Go to management page for a cloud device.
443 * @param {string} device_id ID of device.
445 function manageCloudDevice(device_id) {
446 recordUmaEvent(DEVICES_PAGE_EVENTS.MANAGE_CLICKED);
447 chrome.send('openCloudPrintURL', [device_id]);
451 * Record an event in the UMA histogram.
452 * @param {number} eventId The id of the event to be recorded.
455 function recordUmaEvent(eventId) {
456 chrome.send('metricsHandler:recordInHistogram',
457 ['LocalDiscovery.DevicesPage', eventId, DEVICES_PAGE_EVENTS.MAX_EVENT]);
461 * Cancel the registration.
463 function cancelRegistration() {
464 hideRegisterOverlay();
465 chrome.send('cancelRegistration');
466 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CANCEL);
470 * Retry loading the devices from Google Cloud Print.
472 function retryLoadCloudDevices() {
473 requestPrinterList();
477 * User is not logged in.
479 function setUserLoggedIn(userLoggedIn) {
480 isUserLoggedIn = userLoggedIn;
482 $('cloud-devices-login-promo').hidden = isUserLoggedIn;
483 $('register-overlay-login-promo').hidden = isUserLoggedIn;
484 $('register-continue-button').disabled = !isUserLoggedIn;
486 if (isUserLoggedIn) {
487 requestPrinterList();
488 $('register-login-promo').hidden = true;
490 $('cloud-devices-loading').hidden = true;
491 $('cloud-devices-unavailable').hidden = true;
492 clearElement($('cloud-devices'));
493 hideRegisterOverlay();
496 updateUIToReflectState();
498 for (var device in devices) {
499 devices[device].setRegisterEnabled(isUserLoggedIn);
503 function openSignInPage() {
504 chrome.send('showSyncUI');
507 function registerLoginButtonClicked() {
508 recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_PROMO);
512 function registerOverlayLoginButtonClicked() {
514 DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_OVERLAY_PROMO);
518 function cloudDevicesLoginButtonClicked() {
519 recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO);
524 * Set the Cloud Print proxy UI to enabled, disabled, or processing.
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');
534 $('cloudPrintConnectorSetupButton').textContent =
535 loadTimeData.getString('cloudPrintConnectorEnabledButton');
537 $('cloudPrintConnectorSetupButton').disabled = !allowed;
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');
548 $('cloudPrintConnectorSetupButton').onclick = function(event) {
549 chrome.send('disableCloudPrintConnector');
550 requestPrinterList();
556 function removeCloudPrintConnectorSection() {
557 if (!cr.isChromeOS) {
558 var connectorSectionElm = $('cloud-print-connector-section');
559 if (connectorSectionElm)
560 connectorSectionElm.parentNode.removeChild(connectorSectionElm);
564 function getOverlayIDFromPath() {
565 if (document.location.pathname == '/register') {
566 var params = parseQueryParams(document.location);
567 return params['id'] || null;
571 document.addEventListener('DOMContentLoaded', function() {
572 cr.ui.overlay.setupOverlay($('overlay'));
573 cr.ui.overlay.globalInitialization();
574 $('overlay').addEventListener('cancelOverlay', cancelRegistration);
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);
582 $('register-error-exit').addEventListener('click', cancelRegistration);
585 $('cloud-devices-retry-button').addEventListener('click',
586 retryLoadCloudDevices);
588 $('cloud-devices-login-button').addEventListener(
590 cloudDevicesLoginButtonClicked);
592 $('register-login-button').addEventListener(
594 registerLoginButtonClicked);
596 $('register-overlay-login-button').addEventListener(
598 registerOverlayLoginButtonClicked);
601 document.addEventListener('visibilitychange', updateVisibility, false);
603 focusManager = new LocalDiscoveryFocusManager();
604 focusManager.initialize();
606 chrome.send('start');
607 recordUmaEvent(DEVICES_PAGE_EVENTS.OPENED);
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