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 user is supervised or off the record.
52 var isUserSupervisedOrOffTheRecord
= false;
55 * Whether or not the path-based dialog has been shown.
58 var dialogFromPathHasBeenShown
= false;
61 * Focus manager for page.
63 var focusManager
= null;
66 * Object that represents a device in the device list.
67 * @param {Object} info Information about the device.
70 function Device(info
, registerEnabled
) {
72 this.domElement
= null;
73 this.registerButton
= null;
74 this.registerEnabled
= registerEnabled
;
80 * @param {Object} info New information about the device.
82 updateDevice: function(info
) {
90 removeDevice: function() {
91 this.deviceContainer().removeChild(this.domElement
);
95 * Render the device to the device list.
97 renderDevice: function() {
98 if (this.domElement
) {
99 clearElement(this.domElement
);
101 this.domElement
= document
.createElement('div');
102 this.deviceContainer().appendChild(this.domElement
);
105 this.registerButton
= fillDeviceDescription(
107 this.info
.display_name
,
108 this.info
.description
,
110 loadTimeData
.getString('serviceRegister'),
111 this.showRegister
.bind(this, this.info
.type
));
113 this.setRegisterEnabled(this.registerEnabled
);
117 * Return the correct container for the device.
118 * @param {boolean} is_mine Whether or not the device is in the 'Registered'
121 deviceContainer: function() {
122 return $('register-device-list');
125 * Register the device.
127 register: function() {
128 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_CONFIRMED
);
129 chrome
.send('registerDevice', [this.info
.service_name
]);
130 setRegisterPage(isPrinter(this.info
.type
) ?
131 'register-printer-page-adding1' : 'register-device-page-adding1');
134 * Show registrtation UI for device.
136 showRegister: function() {
137 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_CLICKED
);
138 $('register-message').textContent
= loadTimeData
.getStringF(
139 isPrinter(this.info
.type
) ? 'registerPrinterConfirmMessage' :
140 'registerDeviceConfirmMessage',
141 this.info
.display_name
);
142 $('register-continue-button').onclick
= this.register
.bind(this);
143 showRegisterOverlay();
146 * Set registration button enabled/disabled
148 setRegisterEnabled: function(isEnabled
) {
149 this.registerEnabled
= isEnabled
;
150 if (this.registerButton
) {
151 this.registerButton
.disabled
= !isEnabled
;
157 * Manages focus for local devices page.
159 * @extends {cr.ui.FocusManager}
161 function LocalDiscoveryFocusManager() {
162 cr
.ui
.FocusManager
.call(this);
163 this.focusParent_
= document
.body
;
166 LocalDiscoveryFocusManager
.prototype = {
167 __proto__
: cr
.ui
.FocusManager
.prototype,
169 getFocusParent: function() {
170 return document
.querySelector('#overlay .showing') ||
176 * Returns a textual representation of the number of printers on the network.
177 * @return {string} Number of printers on the network as localized string.
179 function generateNumberPrintersAvailableText(numberPrinters
) {
180 if (numberPrinters
== 0) {
181 return loadTimeData
.getString('printersOnNetworkZero');
182 } else if (numberPrinters
== 1) {
183 return loadTimeData
.getString('printersOnNetworkOne');
185 return loadTimeData
.getStringF('printersOnNetworkMultiple',
191 * Fill device element with the description of a device.
192 * @param {HTMLElement} device_dom_element Element to be filled.
193 * @param {string} name Name of device.
194 * @param {string} description Description of device.
195 * @param {string} type Type of device.
196 * @param {string} button_text Text to appear on button.
197 * @param {function()?} button_action Action for button.
198 * @return {HTMLElement} The button (for enabling/disabling/rebinding)
200 function fillDeviceDescription(device_dom_element
,
206 device_dom_element
.classList
.add('device');
208 device_dom_element
.classList
.add('printer');
210 var deviceInfo
= document
.createElement('div');
211 deviceInfo
.className
= 'device-info';
212 device_dom_element
.appendChild(deviceInfo
);
214 var deviceName
= document
.createElement('h3');
215 deviceName
.className
= 'device-name';
216 deviceName
.textContent
= name
;
217 deviceInfo
.appendChild(deviceName
);
219 var deviceDescription
= document
.createElement('div');
220 deviceDescription
.className
= 'device-subline';
221 deviceDescription
.textContent
= description
;
222 deviceInfo
.appendChild(deviceDescription
);
225 var button
= document
.createElement('button');
226 button
.textContent
= button_text
;
227 button
.addEventListener('click', button_action
);
228 device_dom_element
.appendChild(button
);
235 * Show the register overlay.
237 function showRegisterOverlay() {
238 recordUmaEvent(DEVICES_PAGE_EVENTS
.ADD_PRINTER_CLICKED
);
240 var registerOverlay
= $('register-overlay');
241 registerOverlay
.classList
.add('showing');
242 registerOverlay
.focus();
244 $('overlay').hidden
= false;
245 setRegisterPage('register-page-confirm');
249 * Hide the register overlay.
251 function hideRegisterOverlay() {
252 $('register-overlay').classList
.remove('showing');
253 $('overlay').hidden
= true;
257 * Clear a DOM element of all children.
258 * @param {HTMLElement} element DOM element to clear.
260 function clearElement(element
) {
261 while (element
.firstChild
) {
262 element
.removeChild(element
.firstChild
);
267 * Announce that a registration failed.
269 function onRegistrationFailed() {
270 $('error-message').textContent
=
271 loadTimeData
.getString('addingErrorMessage');
272 setRegisterPage('register-page-error');
273 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_FAILURE
);
277 * Announce that a registration has been canceled on the printer.
279 function onRegistrationCanceledPrinter() {
280 $('error-message').textContent
=
281 loadTimeData
.getString('addingCanceledMessage');
282 setRegisterPage('register-page-error');
283 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_CANCEL_ON_PRINTER
);
287 * Announce that a registration has timed out.
289 function onRegistrationTimeout() {
290 $('error-message').textContent
=
291 loadTimeData
.getString('addingTimeoutMessage');
292 setRegisterPage('register-page-error');
293 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_TIMEOUT
);
297 * Update UI to reflect that registration has been confirmed on the printer.
299 function onRegistrationConfirmedOnPrinter() {
300 setRegisterPage('register-printer-page-adding2');
304 * Shows UI to confirm security code.
305 * @param {string} code The security code to confirm.
307 function onRegistrationConfirmDeviceCode(code
) {
308 setRegisterPage('register-device-page-adding2');
309 $('register-device-page-code').textContent
= code
;
313 * Update device unregistered device list, and update related strings to
314 * reflect the number of devices available to register.
315 * @param {string} name Name of the device.
316 * @param {string} info Additional info of the device or null if the device
319 function onUnregisteredDeviceUpdate(name
, info
) {
321 if (devices
.hasOwnProperty(name
)) {
322 devices
[name
].updateDevice(info
);
324 devices
[name
] = new Device(info
, isUserLoggedIn
);
325 devices
[name
].renderDevice();
328 if (name
== getOverlayIDFromPath() && !dialogFromPathHasBeenShown
) {
329 dialogFromPathHasBeenShown
= true;
330 devices
[name
].showRegister();
333 if (devices
.hasOwnProperty(name
)) {
334 devices
[name
].removeDevice();
335 delete devices
[name
];
339 updateUIToReflectState();
343 * Create the DOM for a cloud device described by the device section.
344 * @param {Array<Object>} devices_list List of devices.
346 function createCloudDeviceDOM(device
) {
347 var devicesDomElement
= document
.createElement('div');
350 if (device
.description
== '') {
351 if (isPrinter(device
.type
))
352 description
= loadTimeData
.getString('noDescriptionPrinter');
354 description
= loadTimeData
.getString('noDescriptionDevice');
356 description
= device
.description
;
359 fillDeviceDescription(devicesDomElement
, device
.display_name
,
360 description
, device
.type
,
361 loadTimeData
.getString('manageDevice'),
362 isPrinter(device
.type
) ?
363 manageCloudDevice
.bind(null, device
.id
) : null);
364 return devicesDomElement
;
368 * Handle a list of cloud devices available to the user globally.
369 * @param {Array<Object>} devices_list List of devices.
371 function onCloudDeviceListAvailable(devices_list
) {
372 var devicesListLength
= devices_list
.length
;
373 var devicesContainer
= $('cloud-devices');
375 clearElement(devicesContainer
);
376 $('cloud-devices-loading').hidden
= true;
378 for (var i
= 0; i
< devicesListLength
; i
++) {
379 devicesContainer
.appendChild(createCloudDeviceDOM(devices_list
[i
]));
384 * Handle the case where the list of cloud devices is not available.
386 function onCloudDeviceListUnavailable() {
387 if (isUserLoggedIn
) {
388 $('cloud-devices-loading').hidden
= true;
389 $('cloud-devices-unavailable').hidden
= false;
394 * Handle the case where the cache for local devices has been flushed..
396 function onDeviceCacheFlushed() {
397 for (var deviceName
in devices
) {
398 devices
[deviceName
].removeDevice();
399 delete devices
[deviceName
];
402 updateUIToReflectState();
406 * Update UI strings to reflect the number of local devices.
408 function updateUIToReflectState() {
409 var numberPrinters
= $('register-device-list').children
.length
;
410 if (numberPrinters
== 0) {
411 $('no-printers-message').hidden
= false;
413 $('register-login-promo').hidden
= true;
415 $('no-printers-message').hidden
= true;
416 $('register-login-promo').hidden
= isUserLoggedIn
||
417 isUserSupervisedOrOffTheRecord
;
422 * Announce that a registration succeeeded.
424 function onRegistrationSuccess(device_data
) {
425 hideRegisterOverlay();
427 if (device_data
.service_name
== getOverlayIDFromPath()) {
431 var deviceDOM
= createCloudDeviceDOM(device_data
);
432 $('cloud-devices').insertBefore(deviceDOM
, $('cloud-devices').firstChild
);
433 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_SUCCESS
);
437 * Update visibility status for page.
439 function updateVisibility() {
440 chrome
.send('isVisible', [!document
.hidden
]);
444 * Set the page that the register wizard is on.
445 * @param {string} page_id ID string for page.
447 function setRegisterPage(page_id
) {
448 var pages
= $('register-overlay').querySelectorAll('.register-page');
449 var pagesLength
= pages
.length
;
450 for (var i
= 0; i
< pagesLength
; i
++) {
451 pages
[i
].hidden
= true;
454 $(page_id
).hidden
= false;
458 * Request the device list.
460 function requestDeviceList() {
461 if (isUserLoggedIn
) {
462 clearElement($('cloud-devices'));
463 $('cloud-devices-loading').hidden
= false;
464 $('cloud-devices-unavailable').hidden
= true;
466 chrome
.send('requestDeviceList');
471 * Go to management page for a cloud device.
472 * @param {string} device_id ID of device.
474 function manageCloudDevice(device_id
) {
475 recordUmaEvent(DEVICES_PAGE_EVENTS
.MANAGE_CLICKED
);
476 chrome
.send('openCloudPrintURL', [device_id
]);
480 * Record an event in the UMA histogram.
481 * @param {number} eventId The id of the event to be recorded.
484 function recordUmaEvent(eventId
) {
485 chrome
.send('metricsHandler:recordInHistogram',
486 ['LocalDiscovery.DevicesPage', eventId
, DEVICES_PAGE_EVENTS
.MAX_EVENT
]);
490 * Cancel the registration.
492 function cancelRegistration() {
493 hideRegisterOverlay();
494 chrome
.send('cancelRegistration');
495 recordUmaEvent(DEVICES_PAGE_EVENTS
.REGISTER_CANCEL
);
499 * Confirms device code.
501 function confirmCode() {
502 chrome
.send('confirmCode');
503 setRegisterPage('register-device-page-adding1');
507 * Retry loading the devices from Google Cloud Print.
509 function retryLoadCloudDevices() {
514 * User is not logged in.
516 function setUserLoggedIn(userLoggedIn
, userSupervisedOrOffTheRecord
) {
517 isUserLoggedIn
= userLoggedIn
;
518 isUserSupervisedOrOffTheRecord
= userSupervisedOrOffTheRecord
;
520 $('cloud-devices-login-promo').hidden
= isUserLoggedIn
||
521 isUserSupervisedOrOffTheRecord
;
522 $('register-overlay-login-promo').hidden
= isUserLoggedIn
||
523 isUserSupervisedOrOffTheRecord
;
524 $('register-continue-button').disabled
= !isUserLoggedIn
||
525 isUserSupervisedOrOffTheRecord
;
527 $('my-devices-container').hidden
= userSupervisedOrOffTheRecord
;
529 if (isUserSupervisedOrOffTheRecord
) {
530 $('cloud-print-connector-section').hidden
= true;
533 if (isUserLoggedIn
&& !isUserSupervisedOrOffTheRecord
) {
535 $('register-login-promo').hidden
= true;
537 $('cloud-devices-loading').hidden
= true;
538 $('cloud-devices-unavailable').hidden
= true;
539 clearElement($('cloud-devices'));
540 hideRegisterOverlay();
543 updateUIToReflectState();
545 for (var device
in devices
) {
546 devices
[device
].setRegisterEnabled(isUserLoggedIn
);
550 function openSignInPage() {
551 chrome
.send('showSyncUI');
554 function registerLoginButtonClicked() {
555 recordUmaEvent(DEVICES_PAGE_EVENTS
.LOG_IN_STARTED_FROM_REGISTER_PROMO
);
559 function registerOverlayLoginButtonClicked() {
561 DEVICES_PAGE_EVENTS
.LOG_IN_STARTED_FROM_REGISTER_OVERLAY_PROMO
);
565 function cloudDevicesLoginButtonClicked() {
566 recordUmaEvent(DEVICES_PAGE_EVENTS
.LOG_IN_STARTED_FROM_DEVICE_LIST_PROMO
);
571 * Set the Cloud Print proxy UI to enabled, disabled, or processing.
574 function setupCloudPrintConnectorSection(disabled
, label
, allowed
) {
575 if (!cr
.isChromeOS
) {
576 $('cloudPrintConnectorLabel').textContent
= label
;
577 if (disabled
|| !allowed
) {
578 $('cloudPrintConnectorSetupButton').textContent
=
579 loadTimeData
.getString('cloudPrintConnectorDisabledButton');
581 $('cloudPrintConnectorSetupButton').textContent
=
582 loadTimeData
.getString('cloudPrintConnectorEnabledButton');
584 $('cloudPrintConnectorSetupButton').disabled
= !allowed
;
587 $('cloudPrintConnectorSetupButton').onclick = function(event
) {
588 // Disable the button, set its text to the intermediate state.
589 $('cloudPrintConnectorSetupButton').textContent
=
590 loadTimeData
.getString('cloudPrintConnectorEnablingButton');
591 $('cloudPrintConnectorSetupButton').disabled
= true;
592 chrome
.send('showCloudPrintSetupDialog');
595 $('cloudPrintConnectorSetupButton').onclick = function(event
) {
596 chrome
.send('disableCloudPrintConnector');
603 function removeCloudPrintConnectorSection() {
604 if (!cr
.isChromeOS
) {
605 var connectorSectionElm
= $('cloud-print-connector-section');
606 if (connectorSectionElm
)
607 connectorSectionElm
.parentNode
.removeChild(connectorSectionElm
);
611 function getOverlayIDFromPath() {
612 if (document
.location
.pathname
== '/register') {
613 var params
= parseQueryParams(document
.location
);
614 return params
['id'] || null;
619 * Returns true of device is printer.
620 * @param {string} type Type of printer.
622 function isPrinter(type
) {
623 return type
== 'printer';
626 document
.addEventListener('DOMContentLoaded', function() {
627 cr
.ui
.overlay
.setupOverlay($('overlay'));
628 cr
.ui
.overlay
.globalInitialization();
629 $('overlay').addEventListener('cancelOverlay', cancelRegistration
);
632 document
.querySelectorAll('.register-cancel'), function(button
) {
633 button
.addEventListener('click', cancelRegistration
);
637 document
.querySelectorAll('.confirm-code'), function(button
) {
638 button
.addEventListener('click', confirmCode
);
641 $('register-error-exit').addEventListener('click', cancelRegistration
);
644 $('cloud-devices-retry-link').addEventListener('click',
645 retryLoadCloudDevices
);
647 $('cloud-devices-login-link').addEventListener(
649 cloudDevicesLoginButtonClicked
);
651 $('register-login-link').addEventListener(
653 registerLoginButtonClicked
);
655 $('register-overlay-login-button').addEventListener(
657 registerOverlayLoginButtonClicked
);
659 if (loadTimeData
.valueExists('backButtonURL')) {
660 $('back-link').hidden
= false;
661 $('back-link').addEventListener('click', function() {
662 window
.location
.href
= loadTimeData
.getString('backButtonURL');
667 document
.addEventListener('visibilitychange', updateVisibility
, false);
669 focusManager
= new LocalDiscoveryFocusManager();
670 focusManager
.initialize();
672 chrome
.send('start');
673 recordUmaEvent(DEVICES_PAGE_EVENTS
.OPENED
);
677 onRegistrationSuccess
: onRegistrationSuccess
,
678 onRegistrationFailed
: onRegistrationFailed
,
679 onUnregisteredDeviceUpdate
: onUnregisteredDeviceUpdate
,
680 onRegistrationConfirmedOnPrinter
: onRegistrationConfirmedOnPrinter
,
681 onRegistrationConfirmDeviceCode
: onRegistrationConfirmDeviceCode
,
682 onCloudDeviceListAvailable
: onCloudDeviceListAvailable
,
683 onCloudDeviceListUnavailable
: onCloudDeviceListUnavailable
,
684 onDeviceCacheFlushed
: onDeviceCacheFlushed
,
685 onRegistrationCanceledPrinter
: onRegistrationCanceledPrinter
,
686 onRegistrationTimeout
: onRegistrationTimeout
,
687 setUserLoggedIn
: setUserLoggedIn
,
688 setupCloudPrintConnectorSection
: setupCloudPrintConnectorSection
,
689 removeCloudPrintConnectorSection
: removeCloudPrintConnectorSection