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.display_name,
102 this.info.description,
104 loadTimeData.getString('serviceRegister'),
105 this.showRegister.bind(this, this.info.type));
107 this.setRegisterEnabled(this.registerEnabled);
111 * Return the correct container for the device.
112 * @param {boolean} is_mine Whether or not the device is in the 'Registered'
115 deviceContainer: function() {
116 return $('register-device-list');
119 * Register the device.
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');
128 * Show registrtation UI for device.
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();
140 * Set registration button enabled/disabled
142 setRegisterEnabled: function(isEnabled) {
143 this.registerEnabled = isEnabled;
144 if (this.registerButton) {
145 this.registerButton.disabled = !isEnabled;
151 * Manages focus for local devices page.
153 * @extends {cr.ui.FocusManager}
155 function LocalDiscoveryFocusManager() {
156 cr.ui.FocusManager.call(this);
157 this.focusParent_ = document.body;
160 LocalDiscoveryFocusManager.prototype = {
161 __proto__: cr.ui.FocusManager.prototype,
163 getFocusParent: function() {
164 return document.querySelector('#overlay .showing') ||
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.
173 function generateNumberPrintersAvailableText(numberPrinters) {
174 if (numberPrinters == 0) {
175 return loadTimeData.getString('printersOnNetworkZero');
176 } else if (numberPrinters == 1) {
177 return loadTimeData.getString('printersOnNetworkOne');
179 return loadTimeData.getStringF('printersOnNetworkMultiple',
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)
194 function fillDeviceDescription(device_dom_element,
200 device_dom_element.classList.add('device');
202 device_dom_element.classList.add('printer');
204 var deviceInfo = document.createElement('div');
205 deviceInfo.className = 'device-info';
206 device_dom_element.appendChild(deviceInfo);
208 var deviceName = document.createElement('h3');
209 deviceName.className = 'device-name';
210 deviceName.textContent = name;
211 deviceInfo.appendChild(deviceName);
213 var deviceDescription = document.createElement('div');
214 deviceDescription.className = 'device-subline';
215 deviceDescription.textContent = description;
216 deviceInfo.appendChild(deviceDescription);
219 var button = document.createElement('button');
220 button.textContent = button_text;
221 button.addEventListener('click', button_action);
222 device_dom_element.appendChild(button);
229 * Show the register overlay.
231 function showRegisterOverlay() {
232 recordUmaEvent(DEVICES_PAGE_EVENTS.ADD_PRINTER_CLICKED);
234 var registerOverlay = $('register-overlay');
235 registerOverlay.classList.add('showing');
236 registerOverlay.focus();
238 $('overlay').hidden = false;
239 setRegisterPage('register-page-confirm');
243 * Hide the register overlay.
245 function hideRegisterOverlay() {
246 $('register-overlay').classList.remove('showing');
247 $('overlay').hidden = true;
251 * Clear a DOM element of all children.
252 * @param {HTMLElement} element DOM element to clear.
254 function clearElement(element) {
255 while (element.firstChild) {
256 element.removeChild(element.firstChild);
261 * Announce that a registration failed.
263 function onRegistrationFailed() {
264 $('error-message').textContent =
265 loadTimeData.getString('addingErrorMessage');
266 setRegisterPage('register-page-error');
267 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_FAILURE);
271 * Announce that a registration has been canceled on the printer.
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);
281 * Announce that a registration has timed out.
283 function onRegistrationTimeout() {
284 $('error-message').textContent =
285 loadTimeData.getString('addingTimeoutMessage');
286 setRegisterPage('register-page-error');
287 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_TIMEOUT);
291 * Update UI to reflect that registration has been confirmed on the printer.
293 function onRegistrationConfirmedOnPrinter() {
294 setRegisterPage('register-printer-page-adding2');
298 * Shows UI to confirm security code.
299 * @param {string} code The security code to confirm.
301 function onRegistrationConfirmDeviceCode(code) {
302 setRegisterPage('register-device-page-adding2');
303 $('register-device-page-code').textContent = code;
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
313 function onUnregisteredDeviceUpdate(name, info) {
315 if (devices.hasOwnProperty(name)) {
316 devices[name].updateDevice(info);
318 devices[name] = new Device(info, isUserLoggedIn);
319 devices[name].renderDevice();
322 if (name == getOverlayIDFromPath() && !dialogFromPathHasBeenShown) {
323 dialogFromPathHasBeenShown = true;
324 devices[name].showRegister();
327 if (devices.hasOwnProperty(name)) {
328 devices[name].removeDevice();
329 delete devices[name];
333 updateUIToReflectState();
337 * Create the DOM for a cloud device described by the device section.
338 * @param {Array.<Object>} devices_list List of devices.
340 function createCloudDeviceDOM(device) {
341 var devicesDomElement = document.createElement('div');
344 if (device.description == '') {
345 if (isPrinter(device.type))
346 description = loadTimeData.getString('noDescriptionPrinter');
348 description = loadTimeData.getString('noDescriptionDevice');
350 description = device.description;
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;
362 * Handle a list of cloud devices available to the user globally.
363 * @param {Array.<Object>} devices_list List of devices.
365 function onCloudDeviceListAvailable(devices_list) {
366 var devicesListLength = devices_list.length;
367 var devicesContainer = $('cloud-devices');
369 clearElement(devicesContainer);
370 $('cloud-devices-loading').hidden = true;
372 for (var i = 0; i < devicesListLength; i++) {
373 devicesContainer.appendChild(createCloudDeviceDOM(devices_list[i]));
378 * Handle the case where the list of cloud devices is not available.
380 function onCloudDeviceListUnavailable() {
381 if (isUserLoggedIn) {
382 $('cloud-devices-loading').hidden = true;
383 $('cloud-devices-unavailable').hidden = false;
388 * Handle the case where the cache for local devices has been flushed..
390 function onDeviceCacheFlushed() {
391 for (var deviceName in devices) {
392 devices[deviceName].removeDevice();
393 delete devices[deviceName];
396 updateUIToReflectState();
400 * Update UI strings to reflect the number of local devices.
402 function updateUIToReflectState() {
403 var numberPrinters = $('register-device-list').children.length;
404 if (numberPrinters == 0) {
405 $('no-printers-message').hidden = false;
407 $('register-login-promo').hidden = true;
409 $('no-printers-message').hidden = true;
410 $('register-login-promo').hidden = isUserLoggedIn;
415 * Announce that a registration succeeeded.
417 function onRegistrationSuccess(device_data) {
418 hideRegisterOverlay();
420 if (device_data.service_name == getOverlayIDFromPath()) {
424 var deviceDOM = createCloudDeviceDOM(device_data);
425 $('cloud-devices').insertBefore(deviceDOM, $('cloud-devices').firstChild);
426 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_SUCCESS);
430 * Update visibility status for page.
432 function updateVisibility() {
433 chrome.send('isVisible', [!document.hidden]);
437 * Set the page that the register wizard is on.
438 * @param {string} page_id ID string for page.
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;
447 $(page_id).hidden = false;
451 * Request the device list.
453 function requestDeviceList() {
454 if (isUserLoggedIn) {
455 clearElement($('cloud-devices'));
456 $('cloud-devices-loading').hidden = false;
457 $('cloud-devices-unavailable').hidden = true;
459 chrome.send('requestDeviceList');
464 * Go to management page for a cloud device.
465 * @param {string} device_id ID of device.
467 function manageCloudDevice(device_id) {
468 recordUmaEvent(DEVICES_PAGE_EVENTS.MANAGE_CLICKED);
469 chrome.send('openCloudPrintURL', [device_id]);
473 * Record an event in the UMA histogram.
474 * @param {number} eventId The id of the event to be recorded.
477 function recordUmaEvent(eventId) {
478 chrome.send('metricsHandler:recordInHistogram',
479 ['LocalDiscovery.DevicesPage', eventId, DEVICES_PAGE_EVENTS.MAX_EVENT]);
483 * Cancel the registration.
485 function cancelRegistration() {
486 hideRegisterOverlay();
487 chrome.send('cancelRegistration');
488 recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CANCEL);
492 * Confirms device code.
494 function confirmCode() {
495 chrome.send('confirmCode');
496 setRegisterPage('register-device-page-adding1');
500 * Retry loading the devices from Google Cloud Print.
502 function retryLoadCloudDevices() {
507 * User is not logged in.
509 function setUserLoggedIn(userLoggedIn) {
510 isUserLoggedIn = userLoggedIn;
512 $('cloud-devices-login-promo').hidden = isUserLoggedIn;
513 $('register-overlay-login-promo').hidden = isUserLoggedIn;
514 $('register-continue-button').disabled = !isUserLoggedIn;
516 if (isUserLoggedIn) {
518 $('register-login-promo').hidden = true;
520 $('cloud-devices-loading').hidden = true;
521 $('cloud-devices-unavailable').hidden = true;
522 clearElement($('cloud-devices'));
523 hideRegisterOverlay();
526 updateUIToReflectState();
528 for (var device in devices) {
529 devices[device].setRegisterEnabled(isUserLoggedIn);
533 function openSignInPage() {
534 chrome.send('showSyncUI');
537 function registerLoginButtonClicked() {
538 recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_PROMO);
542 function registerOverlayLoginButtonClicked() {
544 DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_REGISTER_OVERLAY_PROMO);
548 function cloudDevicesLoginButtonClicked() {
549 recordUmaEvent(DEVICES_PAGE_EVENTS.LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO);
554 * Set the Cloud Print proxy UI to enabled, disabled, or processing.
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');
564 $('cloudPrintConnectorSetupButton').textContent =
565 loadTimeData.getString('cloudPrintConnectorEnabledButton');
567 $('cloudPrintConnectorSetupButton').disabled = !allowed;
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');
578 $('cloudPrintConnectorSetupButton').onclick = function(event) {
579 chrome.send('disableCloudPrintConnector');
586 function removeCloudPrintConnectorSection() {
587 if (!cr.isChromeOS) {
588 var connectorSectionElm = $('cloud-print-connector-section');
589 if (connectorSectionElm)
590 connectorSectionElm.parentNode.removeChild(connectorSectionElm);
594 function getOverlayIDFromPath() {
595 if (document.location.pathname == '/register') {
596 var params = parseQueryParams(document.location);
597 return params['id'] || null;
602 * Returns true of device is printer.
603 * @param {string} type Type of printer.
605 function isPrinter(type) {
606 return type == 'printer';
609 document.addEventListener('DOMContentLoaded', function() {
610 cr.ui.overlay.setupOverlay($('overlay'));
611 cr.ui.overlay.globalInitialization();
612 $('overlay').addEventListener('cancelOverlay', cancelRegistration);
615 document.querySelectorAll('.register-cancel'), function(button) {
616 button.addEventListener('click', cancelRegistration);
620 document.querySelectorAll('.confirm-code'), function(button) {
621 button.addEventListener('click', confirmCode);
624 $('register-error-exit').addEventListener('click', cancelRegistration);
627 $('cloud-devices-retry-button').addEventListener('click',
628 retryLoadCloudDevices);
630 $('cloud-devices-login-button').addEventListener(
632 cloudDevicesLoginButtonClicked);
634 $('register-login-button').addEventListener(
636 registerLoginButtonClicked);
638 $('register-overlay-login-button').addEventListener(
640 registerOverlayLoginButtonClicked);
642 if (loadTimeData.valueExists('backButtonURL')) {
643 $('back-button').hidden = false;
644 $('back-button').addEventListener('click', function() {
645 window.location.href = loadTimeData.getString('backButtonURL');
650 document.addEventListener('visibilitychange', updateVisibility, false);
652 focusManager = new LocalDiscoveryFocusManager();
653 focusManager.initialize();
655 chrome.send('start');
656 recordUmaEvent(DEVICES_PAGE_EVENTS.OPENED);
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