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 |