| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Javascript for local_discovery.html, served from chrome://devices/ | 6 * Javascript for local_discovery.html, served from chrome://devices/ |
| 7 * This is used to show discoverable devices near the user. | 7 * This is used to show discoverable devices near the user as well as |
| 8 * cloud devices registered to them. |
| 8 * | 9 * |
| 9 * The simple object defined in this javascript file listens for | 10 * The object defined in this javascript file listens for callbacks from the |
| 10 * callbacks from the C++ code saying that a new device is available. | 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. |
| 11 */ | 13 */ |
| 12 | 14 |
| 13 | 15 |
| 14 <include src="../uber/uber_utils.js" /> | 16 <include src="../uber/uber_utils.js" /> |
| 15 | 17 |
| 16 cr.define('local_discovery', function() { | 18 cr.define('local_discovery', function() { |
| 17 'use strict'; | 19 'use strict'; |
| 18 | 20 |
| 19 /** | 21 /** |
| 22 * Prefix for printer management page URLs, relative to base cloud print URL. |
| 23 * @type {string} |
| 24 */ |
| 25 var PRINTER_MANAGEMENT_PAGE_PREFIX = '#printer/id/'; |
| 26 |
| 27 /** |
| 20 * Map of service names to corresponding service objects. | 28 * Map of service names to corresponding service objects. |
| 21 * @type {Object.<string,Service>} | 29 * @type {Object.<string,Service>} |
| 22 */ | 30 */ |
| 23 var devices = {}; | 31 var devices = {}; |
| 24 | 32 |
| 25 | |
| 26 /** | 33 /** |
| 27 * Object that represents a device in the device list. | 34 * Object that represents a device in the device list. |
| 28 * @param {Object} info Information about the device. | 35 * @param {Object} info Information about the device. |
| 29 * @constructor | 36 * @constructor |
| 30 */ | 37 */ |
| 31 function Device(info) { | 38 function Device(info) { |
| 32 this.info = info; | 39 this.info = info; |
| 33 this.domElement = null; | 40 this.domElement = null; |
| 34 } | 41 } |
| 35 | 42 |
| 36 Device.prototype = { | 43 Device.prototype = { |
| 37 /** | 44 /** |
| 38 * Update the device. | 45 * Update the device. |
| 39 * @param {Object} info New information about the device. | 46 * @param {Object} info New information about the device. |
| 40 */ | 47 */ |
| 41 updateDevice: function(info) { | 48 updateDevice: function(info) { |
| 42 if (this.domElement && info.is_mine != this.info.is_mine) { | |
| 43 this.deviceContainer(this.info.is_mine).removeChild(this.domElement); | |
| 44 this.deviceContainer(info.is_mine).appendChild(this.domElement); | |
| 45 } | |
| 46 this.info = info; | 49 this.info = info; |
| 47 this.renderDevice(); | 50 this.renderDevice(); |
| 48 }, | 51 }, |
| 49 /** | 52 /** |
| 50 * Delete the device. | 53 * Delete the device. |
| 51 */ | 54 */ |
| 52 removeDevice: function() { | 55 removeDevice: function() { |
| 53 this.deviceContainer(this.info.is_mine).removeChild(this.domElement); | 56 this.deviceContainer().removeChild(this.domElement); |
| 54 }, | 57 }, |
| 55 /** | 58 /** |
| 56 * Render the device to the device list. | 59 * Render the device to the device list. |
| 57 */ | 60 */ |
| 58 renderDevice: function() { | 61 renderDevice: function() { |
| 59 if (this.domElement) { | 62 if (this.domElement) { |
| 60 clearElement(this.domElement); | 63 clearElement(this.domElement); |
| 61 } else { | 64 } else { |
| 62 this.domElement = document.createElement('div'); | 65 this.domElement = document.createElement('div'); |
| 63 this.deviceContainer(this.info.is_mine).appendChild(this.domElement); | 66 this.deviceContainer().appendChild(this.domElement); |
| 64 } | 67 } |
| 65 | 68 |
| 66 this.domElement.classList.add('device'); | 69 fillDeviceDescription(this.domElement, this.info.human_readable_name, |
| 67 this.domElement.classList.add('printer-active'); | 70 this.info.description, |
| 68 | 71 loadTimeData.getString('serviceRegister'), |
| 69 var deviceInfo = document.createElement('div'); | 72 this.register.bind(this)); |
| 70 deviceInfo.className = 'device-info'; | |
| 71 this.domElement.appendChild(deviceInfo); | |
| 72 | |
| 73 var deviceName = document.createElement('h3'); | |
| 74 deviceName.className = 'device-name'; | |
| 75 deviceName.textContent = this.info.human_readable_name; | |
| 76 deviceInfo.appendChild(deviceName); | |
| 77 | |
| 78 var deviceDescription = document.createElement('div'); | |
| 79 deviceDescription.className = 'device-description'; | |
| 80 deviceDescription.textContent = this.info.description; | |
| 81 deviceInfo.appendChild(deviceDescription); | |
| 82 | |
| 83 var buttonContainer = document.createElement('div'); | |
| 84 buttonContainer.className = 'button-container'; | |
| 85 this.domElement.appendChild(buttonContainer); | |
| 86 | |
| 87 var button = document.createElement('button'); | |
| 88 button.textContent = loadTimeData.getString('serviceRegister'); | |
| 89 | |
| 90 if (this.info.registered) { | |
| 91 button.disabled = 'disabled'; | |
| 92 } else { | |
| 93 button.addEventListener( | |
| 94 'click', | |
| 95 sendRegisterDevice.bind(null, this.info.service_name)); | |
| 96 } | |
| 97 | |
| 98 buttonContainer.appendChild(button); | |
| 99 }, | 73 }, |
| 100 /** | 74 /** |
| 101 * Return the correct container for the device. | 75 * Return the correct container for the device. |
| 102 * @param {boolean} is_mine Whether or not the device is in the 'Registered' | 76 * @param {boolean} is_mine Whether or not the device is in the 'Registered' |
| 103 * section. | 77 * section. |
| 104 */ | 78 */ |
| 105 deviceContainer: function(is_mine) { | 79 deviceContainer: function() { |
| 106 if (is_mine) return $('registered-devices'); | 80 return $('register-device-list'); |
| 107 return $('unregistered-devices'); | 81 }, |
| 82 /** |
| 83 * Register the device. |
| 84 */ |
| 85 register: function() { |
| 86 chrome.send('registerDevice', [this.info.service_name]); |
| 87 setRegisterPage('register-page-adding1'); |
| 108 } | 88 } |
| 109 }; | 89 }; |
| 110 | 90 |
| 111 /** | 91 /** |
| 112 * Appends a row to the output table listing the new device. | 92 * Returns a textual representation of the number of printers on the network. |
| 113 * @param {string} name Name of the device. | 93 * @return {string} Number of printers on the network as localized string. |
| 114 * @param {string} info Additional info of the device, if empty device need to | |
| 115 * be deleted. | |
| 116 */ | 94 */ |
| 117 function onDeviceUpdate(name, info) { | 95 function generateNumberPrintersAvailableText(numberPrinters) { |
| 118 if (info) { | 96 if (numberPrinters == 0) { |
| 119 if (devices.hasOwnProperty(name)) { | 97 return loadTimeData.getString('printersOnNetworkZero'); |
| 120 devices[name].updateDevice(info); | 98 } else if (numberPrinters == 1) { |
| 121 } else { | 99 return loadTimeData.getString('printersOnNetworkOne'); |
| 122 devices[name] = new Device(info); | |
| 123 devices[name].renderDevice(); | |
| 124 } | |
| 125 } else { | 100 } else { |
| 126 devices[name].removeDevice(); | 101 return loadTimeData.getStringF('printersOnNetworkMultiple', |
| 127 delete devices[name]; | 102 numberPrinters); |
| 128 } | 103 } |
| 129 } | 104 } |
| 130 | 105 |
| 131 /** | 106 /** |
| 107 * Fill device element with the description of a device. |
| 108 * @param {HTMLElement} device_dom_element Element to be filled. |
| 109 * @param {string} name Name of device. |
| 110 * @param {string} description Description of device. |
| 111 * @param {string} button_text Text to appear on button. |
| 112 * @param {function()} button_action Action for button. |
| 113 */ |
| 114 function fillDeviceDescription(device_dom_element, |
| 115 name, |
| 116 description, |
| 117 button_text, |
| 118 button_action) { |
| 119 device_dom_element.classList.add('device'); |
| 120 |
| 121 var deviceInfo = document.createElement('div'); |
| 122 deviceInfo.className = 'device-info'; |
| 123 device_dom_element.appendChild(deviceInfo); |
| 124 |
| 125 var deviceName = document.createElement('h3'); |
| 126 deviceName.className = 'device-name'; |
| 127 deviceName.textContent = name; |
| 128 deviceInfo.appendChild(deviceName); |
| 129 |
| 130 var deviceDescription = document.createElement('div'); |
| 131 deviceDescription.className = 'device-subline'; |
| 132 deviceDescription.textContent = description; |
| 133 deviceInfo.appendChild(deviceDescription); |
| 134 |
| 135 var button = document.createElement('button'); |
| 136 button.textContent = button_text; |
| 137 button.addEventListener('click', button_action); |
| 138 device_dom_element.appendChild(button); |
| 139 } |
| 140 |
| 141 /** |
| 132 * Hide the register overlay. | 142 * Hide the register overlay. |
| 133 */ | 143 */ |
| 134 function showRegisterOverlay() { | 144 function showRegisterOverlay() { |
| 135 $('register-overlay').classList.add('showing'); | 145 $('register-overlay').classList.add('showing'); |
| 136 $('overlay').hidden = false; | 146 $('overlay').hidden = false; |
| 137 uber.invokeMethodOnParent('beginInterceptingEvents'); | 147 uber.invokeMethodOnParent('beginInterceptingEvents'); |
| 148 setRegisterPage('register-page-choose'); |
| 138 } | 149 } |
| 139 | 150 |
| 140 /** | 151 /** |
| 141 * Show the register overlay. | 152 * Show the register overlay. |
| 142 */ | 153 */ |
| 143 function hideRegisterOverlay() { | 154 function hideRegisterOverlay() { |
| 144 $('register-overlay').classList.remove('showing'); | 155 $('register-overlay').classList.remove('showing'); |
| 145 $('overlay').hidden = true; | 156 $('overlay').hidden = true; |
| 146 uber.invokeMethodOnParent('stopInterceptingEvents'); | 157 uber.invokeMethodOnParent('stopInterceptingEvents'); |
| 147 chrome.send('cancelRegistration'); | 158 chrome.send('cancelRegistration'); |
| 148 } | 159 } |
| 149 | 160 |
| 150 /** | 161 /** |
| 151 * Clear a DOM element of all children. | 162 * Clear a DOM element of all children. |
| 152 * @param {HTMLElement} element DOM element to clear. | 163 * @param {HTMLElement} element DOM element to clear. |
| 153 */ | 164 */ |
| 154 function clearElement(element) { | 165 function clearElement(element) { |
| 155 while (element.firstChild) { | 166 while (element.firstChild) { |
| 156 element.removeChild(element.firstChild); | 167 element.removeChild(element.firstChild); |
| 157 } | 168 } |
| 158 } | 169 } |
| 159 | 170 |
| 160 /** | 171 /** |
| 161 * Register a device. | |
| 162 * @param {string} device The device to register. | |
| 163 */ | |
| 164 function sendRegisterDevice(device) { | |
| 165 chrome.send('registerDevice', [device]); | |
| 166 } | |
| 167 | |
| 168 /** | |
| 169 * Announce that a registration failed. | 172 * Announce that a registration failed. |
| 170 */ | 173 */ |
| 171 function registrationFailed() { | 174 function onRegistrationFailed() { |
| 172 setRegisterPage('register-page-error'); | 175 setRegisterPage('register-page-error'); |
| 173 } | 176 } |
| 174 | 177 |
| 175 /** | 178 /** |
| 176 * Update UI to reflect that registration has been confirmed on the printer. | 179 * Update UI to reflect that registration has been confirmed on the printer. |
| 177 */ | 180 */ |
| 178 function registrationConfirmedOnPrinter() { | 181 function onRegistrationConfirmedOnPrinter() { |
| 179 setRegisterPage('register-page-adding2'); | 182 setRegisterPage('register-page-adding2'); |
| 180 } | 183 } |
| 181 | 184 |
| 182 /** | 185 /** |
| 183 * Announce that a registration succeeeded. | 186 * Update device unregistered device list, and update related strings to |
| 187 * reflect the number of devices available to register. |
| 188 * @param {string} name Name of the device. |
| 189 * @param {string} info Additional info of the device or null if the device |
| 190 * has been removed. |
| 184 */ | 191 */ |
| 185 function registrationSuccess() { | 192 function onUnregisteredDeviceUpdate(name, info) { |
| 186 hideRegisterOverlay(); | 193 if (info) { |
| 194 if (devices.hasOwnProperty(name)) { |
| 195 devices[name].updateDevice(info); |
| 196 } else { |
| 197 devices[name] = new Device(info); |
| 198 devices[name].renderDevice(); |
| 199 } |
| 200 } else { |
| 201 if (devices.hasOwnProperty(name)) { |
| 202 devices[name].removeDevice(); |
| 203 delete devices[name]; |
| 204 } |
| 205 } |
| 206 |
| 207 var numberPrinters = $('register-device-list').children.length; |
| 208 $('printer-num').textContent = generateNumberPrintersAvailableText( |
| 209 numberPrinters); |
| 210 |
| 211 if (numberPrinters == 0) { |
| 212 $('register-message').textContent = loadTimeData.getString( |
| 213 'noPrintersOnNetworkExplanation'); |
| 214 } else { |
| 215 $('register-message').textContent = loadTimeData.getString( |
| 216 'registerConfirmMessage'); |
| 217 } |
| 187 } | 218 } |
| 188 | 219 |
| 189 /** | 220 /** |
| 221 * Handle a list of cloud devices available to the user globally. |
| 222 * @param {Array.<Object>} devices_list List of devices. |
| 223 */ |
| 224 function onCloudDeviceListAvailable(devices_list) { |
| 225 var devicesListLength = devices_list.length; |
| 226 var devicesContainer = $('cloud-devices'); |
| 227 |
| 228 clearElement(devicesContainer); |
| 229 $('cloud-devices-loading').hidden = true; |
| 230 |
| 231 for (var i = 0; i < devicesListLength; i++) { |
| 232 var devicesDomElement = document.createElement('div'); |
| 233 devicesContainer.appendChild(devicesDomElement); |
| 234 |
| 235 var description; |
| 236 if (devices_list[i].description == '') { |
| 237 description = loadTimeData.getString('noDescription'); |
| 238 } else { |
| 239 description = devices_list[i].description; |
| 240 } |
| 241 |
| 242 fillDeviceDescription(devicesDomElement, devices_list[i].display_name, |
| 243 description, 'Manage' /*Localize*/, |
| 244 manageCloudDevice.bind(null, devices_list[i].id)); |
| 245 } |
| 246 } |
| 247 |
| 248 |
| 249 /** |
| 250 * Announce that a registration succeeeded. |
| 251 */ |
| 252 function onRegistrationSuccess() { |
| 253 hideRegisterOverlay(); |
| 254 requestPrinterList(); |
| 255 } |
| 256 |
| 257 /** |
| 190 * Update visibility status for page. | 258 * Update visibility status for page. |
| 191 */ | 259 */ |
| 192 function updateVisibility() { | 260 function updateVisibility() { |
| 193 chrome.send('isVisible', [!document.webkitHidden]); | 261 chrome.send('isVisible', [!document.webkitHidden]); |
| 194 } | 262 } |
| 195 | 263 |
| 196 /** | 264 /** |
| 197 * Set the page that the register wizard is on. | 265 * Set the page that the register wizard is on. |
| 198 * @param {string} page_id ID string for page. | 266 * @param {string} page_id ID string for page. |
| 199 */ | 267 */ |
| 200 function setRegisterPage(page_id) { | 268 function setRegisterPage(page_id) { |
| 201 var pages = $('register-overlay').querySelectorAll('.register-page'); | 269 var pages = $('register-overlay').querySelectorAll('.register-page'); |
| 202 var pagesLength = pages.length; | 270 var pagesLength = pages.length; |
| 203 for (var i = 0; i < pagesLength; i++) { | 271 for (var i = 0; i < pagesLength; i++) { |
| 204 pages[i].hidden = true; | 272 pages[i].hidden = true; |
| 205 } | 273 } |
| 206 | 274 |
| 207 $(page_id).hidden = false; | 275 $(page_id).hidden = false; |
| 208 } | 276 } |
| 209 | 277 |
| 210 /** | 278 /** |
| 211 * Request a user account from a list. | 279 * Request the printer list. |
| 212 * @param {Array} users Array of (index, username) tuples. Username may be | |
| 213 * displayed to the user; index must be passed opaquely to the UI handler. | |
| 214 */ | 280 */ |
| 215 function requestUser(users, printerName) { | 281 function requestPrinterList() { |
| 216 clearElement($('register-user-list')); | 282 clearElement($('cloud-devices')); |
| 217 | 283 $('cloud-devices-loading').hidden = false; |
| 218 var usersLength = users.length; | 284 chrome.send('requestPrinterList'); |
| 219 for (var i = 0; i < usersLength; i++) { | |
| 220 var userIndex = users[i][0]; | |
| 221 var userName = users[i][1]; | |
| 222 | |
| 223 var option = document.createElement('option'); | |
| 224 option.textContent = userName; | |
| 225 option.userData = { userIndex: userIndex, userName: userName }; | |
| 226 $('register-user-list').appendChild(option); | |
| 227 } | |
| 228 | |
| 229 showRegisterOverlay(); | |
| 230 setRegisterPage('register-page-choose'); | |
| 231 $('register-message').textContent = | |
| 232 loadTimeData.getStringF('registerConfirmMessage', printerName); | |
| 233 } | 285 } |
| 234 | 286 |
| 235 /** | 287 /** |
| 236 * Send user selection and begin registration. | 288 * Go to management page for a cloud device. |
| 289 * @param {string} device_id ID of device. |
| 237 */ | 290 */ |
| 238 function beginRegistration() { | 291 function manageCloudDevice(device_id) { |
| 239 var userList = $('register-user-list'); | 292 chrome.send('openCloudPrintURL', |
| 240 var selectedOption = userList.options[userList.selectedIndex]; | 293 [PRINTER_MANAGEMENT_PAGE_PREFIX + device_id]); |
| 241 var userData = selectedOption.userData; | |
| 242 chrome.send('chooseUser', [userData.userIndex, userData.userName]); | |
| 243 setRegisterPage('register-page-adding1'); | |
| 244 } | 294 } |
| 245 | 295 |
| 296 |
| 246 document.addEventListener('DOMContentLoaded', function() { | 297 document.addEventListener('DOMContentLoaded', function() { |
| 247 uber.onContentFrameLoaded(); | 298 uber.onContentFrameLoaded(); |
| 248 | 299 |
| 249 cr.ui.overlay.setupOverlay($('overlay')); | 300 cr.ui.overlay.setupOverlay($('overlay')); |
| 250 cr.ui.overlay.globalInitialization(); | 301 cr.ui.overlay.globalInitialization(); |
| 251 $('overlay').addEventListener('cancelOverlay', hideRegisterOverlay); | 302 $('overlay').addEventListener('cancelOverlay', hideRegisterOverlay); |
| 252 | 303 |
| 253 var cancelButtons = document.querySelectorAll('.register-cancel'); | 304 var cancelButtons = document.querySelectorAll('.register-cancel'); |
| 254 var cancelButtonsLength = cancelButtons.length; | 305 var cancelButtonsLength = cancelButtons.length; |
| 255 for (var i = 0; i < cancelButtonsLength; i++) { | 306 for (var i = 0; i < cancelButtonsLength; i++) { |
| 256 cancelButtons[i].addEventListener('click', hideRegisterOverlay); | 307 cancelButtons[i].addEventListener('click', hideRegisterOverlay); |
| 257 } | 308 } |
| 258 | 309 |
| 259 $('register-error-exit').addEventListener('click', hideRegisterOverlay); | 310 $('register-error-exit').addEventListener('click', hideRegisterOverlay); |
| 260 | 311 |
| 261 $('register-confirmation-continue').addEventListener( | 312 $('add-printers-button').addEventListener('click', |
| 262 'click', beginRegistration); | 313 showRegisterOverlay); |
| 263 | 314 |
| 264 updateVisibility(); | 315 updateVisibility(); |
| 265 document.addEventListener('webkitvisibilitychange', updateVisibility, | 316 document.addEventListener('webkitvisibilitychange', updateVisibility, |
| 266 false); | 317 false); |
| 267 | 318 |
| 268 var title = loadTimeData.getString('devicesTitle'); | 319 var title = loadTimeData.getString('devicesTitle'); |
| 269 uber.invokeMethodOnParent('setTitle', {title: title}); | 320 uber.invokeMethodOnParent('setTitle', {title: title}); |
| 270 | 321 |
| 271 chrome.send('start'); | 322 chrome.send('start'); |
| 323 requestPrinterList(); |
| 272 }); | 324 }); |
| 273 | 325 |
| 274 return { | 326 return { |
| 275 registrationSuccess: registrationSuccess, | 327 onRegistrationSuccess: onRegistrationSuccess, |
| 276 registrationFailed: registrationFailed, | 328 onRegistrationFailed: onRegistrationFailed, |
| 277 onDeviceUpdate: onDeviceUpdate, | 329 onUnregisteredDeviceUpdate: onUnregisteredDeviceUpdate, |
| 278 requestUser: requestUser, | 330 onRegistrationConfirmedOnPrinter: onRegistrationConfirmedOnPrinter, |
| 279 registrationConfirmedOnPrinter: registrationConfirmedOnPrinter | 331 onCloudDeviceListAvailable: onCloudDeviceListAvailable |
| 280 }; | 332 }; |
| 281 }); | 333 }); |
| OLD | NEW |