OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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. | |
4 | |
5 cr.define('options', function() { | |
6 /** @const */ var OptionsPage = options.OptionsPage; | |
7 | |
8 /** | |
9 * Enumeration of possible states during pairing. The value associated with | |
10 * each state maps to a localized string in the global variable | |
11 * |loadTimeData|. | |
12 * @enum {string} | |
13 */ | |
14 var PAIRING = { | |
15 STARTUP: 'bluetoothStartConnecting', | |
16 ENTER_PIN_CODE: 'bluetoothEnterPinCode', | |
17 ENTER_PASSKEY: 'bluetoothEnterPasskey', | |
18 REMOTE_PIN_CODE: 'bluetoothRemotePinCode', | |
19 REMOTE_PASSKEY: 'bluetoothRemotePasskey', | |
20 CONFIRM_PASSKEY: 'bluetoothConfirmPasskey', | |
21 }; | |
22 | |
23 /** | |
24 * List of IDs for conditionally visible elements in the dialog. | |
25 * @type {Array.<String>} | |
26 * @const | |
27 */ | |
28 var ELEMENTS = ['bluetooth-pairing-passkey-display', | |
29 'bluetooth-pairing-passkey-entry', | |
30 'bluetooth-pairing-pincode-entry', | |
31 'bluetooth-pair-device-connect-button', | |
32 'bluetooth-pair-device-cancel-button', | |
33 'bluetooth-pair-device-accept-button', | |
34 'bluetooth-pair-device-reject-button', | |
35 'bluetooth-pair-device-dismiss-button']; | |
36 | |
37 /** | |
38 * Encapsulated handling of the Bluetooth device pairing page. | |
39 * @constructor | |
40 */ | |
41 function BluetoothPairing() { | |
42 OptionsPage.call(this, | |
43 'bluetoothPairing', | |
44 loadTimeData.getString('bluetoothOptionsPageTabTitle'), | |
45 'bluetooth-pairing'); | |
46 } | |
47 | |
48 cr.addSingletonGetter(BluetoothPairing); | |
49 | |
50 BluetoothPairing.prototype = { | |
51 __proto__: OptionsPage.prototype, | |
52 | |
53 /** | |
54 * Description of the bluetooth device. | |
55 * @type {{name: string, | |
56 * address: string, | |
57 * icon: Constants.DEVICE_TYPE, | |
58 * paired: boolean, | |
59 * bonded: boolean, | |
60 * connected: boolean, | |
61 * pairing: string|undefined, | |
62 * passkey: number|undefined, | |
63 * pincode: string|undefined, | |
64 * entered: number|undefined}} | |
65 * @private. | |
66 */ | |
67 device_: null, | |
68 | |
69 /** | |
70 * Can the dialog be programmatically dismissed. | |
71 * @type {boolean} | |
72 */ | |
73 dismissible_: true, | |
74 | |
75 /** @inheritDoc */ | |
76 initializePage: function() { | |
77 OptionsPage.prototype.initializePage.call(this); | |
78 var self = this; | |
79 $('bluetooth-pair-device-cancel-button').onclick = function() { | |
80 chrome.send('updateBluetoothDevice', | |
81 [self.device_.address, 'cancel']); | |
82 OptionsPage.closeOverlay(); | |
83 }; | |
84 $('bluetooth-pair-device-reject-button').onclick = function() { | |
85 chrome.send('updateBluetoothDevice', | |
86 [self.device_.address, 'reject']); | |
87 OptionsPage.closeOverlay(); | |
88 }; | |
89 $('bluetooth-pair-device-connect-button').onclick = function() { | |
90 var args = [self.device_.address, 'connect']; | |
91 var passkey = self.device_.passkey; | |
92 if (passkey) | |
93 args.push(String(passkey)); | |
94 else if (!$('bluetooth-pairing-passkey-entry').hidden) | |
95 args.push($('bluetooth-passkey').value); | |
96 else if (!$('bluetooth-pairing-pincode-entry').hidden) | |
97 args.push($('bluetooth-pincode').value); | |
98 chrome.send('updateBluetoothDevice', args); | |
99 OptionsPage.closeOverlay(); | |
100 }; | |
101 $('bluetooth-pair-device-accept-button').onclick = function() { | |
102 chrome.send('updateBluetoothDevice', | |
103 [self.device_.address, 'accept']); | |
104 OptionsPage.closeOverlay(); | |
105 }; | |
106 $('bluetooth-pair-device-dismiss-button').onclick = function() { | |
107 OptionsPage.closeOverlay(); | |
108 }; | |
109 $('bluetooth-passkey').oninput = function() { | |
110 var inputField = $('bluetooth-passkey'); | |
111 var value = inputField.value; | |
112 // Note that using <input type="number"> is insufficient to restrict | |
113 // the input as it allows negative numbers and does not limit the | |
114 // number of charactes typed even if a range is set. Furthermore, | |
115 // it sometimes produces strange repaint artifacts. | |
116 var filtered = value.replace(/[^0-9]/g, ''); | |
117 if (filtered != value) | |
118 inputField.value = filtered; | |
119 $('bluetooth-pair-device-connect-button').disabled = | |
120 inputField.value.length == 0; | |
121 } | |
122 $('bluetooth-pincode').oninput = function() { | |
123 $('bluetooth-pair-device-connect-button').disabled = | |
124 $('bluetooth-pincode').value.length == 0; | |
125 } | |
126 $('bluetooth-passkey').addEventListener('keydown', | |
127 this.keyDownEventHandler_.bind(this)); | |
128 $('bluetooth-pincode').addEventListener('keydown', | |
129 this.keyDownEventHandler_.bind(this)); | |
130 }, | |
131 | |
132 /** | |
133 * Override to prevent showing the overlay if the Bluetooth device details | |
134 * have not been specified. Prevents showing an empty dialog if the user | |
135 * quits and restarts Chrome while in the process of pairing with a device. | |
136 * @return {boolean} True if the overlay can be displayed. | |
137 */ | |
138 canShowPage: function() { | |
139 return this.device_ && this.device_.address && this.device_.pairing; | |
140 }, | |
141 | |
142 /** | |
143 * Sets input focus on the passkey or pincode field if appropriate. | |
144 */ | |
145 didShowPage: function() { | |
146 if (!$('bluetooth-pincode').hidden) | |
147 $('bluetooth-pincode').focus(); | |
148 else if (!$('bluetooth-passkey').hidden) | |
149 $('bluetooth-passkey').focus(); | |
150 }, | |
151 | |
152 /** | |
153 * Configures the overlay for pairing a device. | |
154 * @param {Object} device Description of the bluetooth device. | |
155 */ | |
156 update: function(device) { | |
157 this.device_ = {}; | |
158 for (key in device) | |
159 this.device_[key] = device[key]; | |
160 // Update the pairing instructions. | |
161 var instructionsEl = $('bluetooth-pairing-instructions'); | |
162 this.clearElement_(instructionsEl); | |
163 this.dismissible_ = ('dismissible' in device) ? | |
164 device.dismissible : true; | |
165 | |
166 var message = loadTimeData.getString(device.pairing); | |
167 message = message.replace('%1', this.device_.name); | |
168 instructionsEl.textContent = message; | |
169 | |
170 // Update visibility of dialog elements. | |
171 if (this.device_.passkey) { | |
172 this.updatePasskey_(); | |
173 if (this.device_.pairing == PAIRING.CONFIRM_PASSKEY) { | |
174 // Confirming a match between displayed passkeys. | |
175 this.displayElements_(['bluetooth-pairing-passkey-display', | |
176 'bluetooth-pair-device-accept-button', | |
177 'bluetooth-pair-device-reject-button']); | |
178 } else { | |
179 // Remote entering a passkey. | |
180 this.displayElements_(['bluetooth-pairing-passkey-display', | |
181 'bluetooth-pair-device-cancel-button']); | |
182 } | |
183 } else if (this.device_.pincode) { | |
184 this.updatePinCode_(); | |
185 this.displayElements_(['bluetooth-pairing-passkey-display', | |
186 'bluetooth-pair-device-cancel-button']); | |
187 } else if (this.device_.pairing == PAIRING.ENTER_PIN_CODE) { | |
188 // Prompting the user to enter a PIN code. | |
189 this.displayElements_(['bluetooth-pairing-pincode-entry', | |
190 'bluetooth-pair-device-connect-button', | |
191 'bluetooth-pair-device-cancel-button']); | |
192 $('bluetooth-pincode').value = ''; | |
193 } else if (this.device_.pairing == PAIRING.ENTER_PASSKEY) { | |
194 // Prompting the user to enter a passkey. | |
195 this.displayElements_(['bluetooth-pairing-passkey-entry', | |
196 'bluetooth-pair-device-connect-button', | |
197 'bluetooth-pair-device-cancel-button']); | |
198 $('bluetooth-passkey').value = ''; | |
199 } else if (this.device_.pairing == PAIRING.STARTUP) { | |
200 // Starting the pairing process. | |
201 this.displayElements_(['bluetooth-pair-device-cancel-button']); | |
202 } else { | |
203 // Displaying an error message. | |
204 this.displayElements_(['bluetooth-pair-device-dismiss-button']); | |
205 } | |
206 // User is required to enter a passkey or pincode before the connect | |
207 // button can be enabled. The 'oninput' methods for the input fields | |
208 // determine when the connect button becomes active. | |
209 $('bluetooth-pair-device-connect-button').disabled = true; | |
210 }, | |
211 | |
212 /** | |
213 * Handles the ENTER key for the passkey or pincode entry field. | |
214 * @return {Event} a keydown event. | |
215 * @private | |
216 */ | |
217 keyDownEventHandler_: function(event) { | |
218 /** @const */ var ENTER_KEY_CODE = 13; | |
219 if (event.keyCode == ENTER_KEY_CODE) { | |
220 var button = $('bluetooth-pair-device-connect-button'); | |
221 if (!button.hidden) | |
222 button.click(); | |
223 } | |
224 }, | |
225 | |
226 /** | |
227 * Updates the visibility of elements in the dialog. | |
228 * @param {Array.<string>} list List of conditionally visible elements that | |
229 * are to be made visible. | |
230 * @private | |
231 */ | |
232 displayElements_: function(list) { | |
233 var enabled = {}; | |
234 for (var i = 0; i < list.length; i++) { | |
235 var key = list[i]; | |
236 enabled[key] = true; | |
237 } | |
238 for (var i = 0; i < ELEMENTS.length; i++) { | |
239 var key = ELEMENTS[i]; | |
240 $(key).hidden = !enabled[key]; | |
241 } | |
242 }, | |
243 | |
244 /** | |
245 * Removes all children from an element. | |
246 * @param {!Element} element Target element to clear. | |
247 */ | |
248 clearElement_: function(element) { | |
249 var child = element.firstChild; | |
250 while (child) { | |
251 element.removeChild(child); | |
252 child = element.firstChild; | |
253 } | |
254 }, | |
255 | |
256 /** | |
257 * Formats an element for displaying the passkey. | |
258 */ | |
259 updatePasskey_: function() { | |
260 var passkeyEl = $('bluetooth-pairing-passkey-display'); | |
261 var keyClass = this.device_.pairing == PAIRING.REMOTE_PASSKEY ? | |
262 'bluetooth-keyboard-button' : 'bluetooth-passkey-char'; | |
263 this.clearElement_(passkeyEl); | |
264 var key = String(this.device_.passkey); | |
265 var progress = this.device_.entered | 0; | |
266 for (var i = 0; i < key.length; i++) { | |
267 var keyEl = document.createElement('span'); | |
268 keyEl.textContent = key.charAt(i); | |
269 keyEl.className = keyClass; | |
270 if (i < progress) | |
271 keyEl.classList.add('key-typed'); | |
272 passkeyEl.appendChild(keyEl); | |
273 } | |
274 if (this.device_.pairing == PAIRING.REMOTE_PASSKEY) { | |
275 // Add enter key. | |
276 var label = loadTimeData.getString('bluetoothEnterKey'); | |
277 var keyEl = document.createElement('span'); | |
278 keyEl.textContent = label; | |
279 keyEl.className = keyClass; | |
280 keyEl.id = 'bluetooth-enter-key'; | |
281 passkeyEl.appendChild(keyEl); | |
282 } | |
283 passkeyEl.hidden = false; | |
284 }, | |
285 | |
286 /** | |
287 * Formats an element for displaying the PIN code. | |
288 */ | |
289 updatePinCode_: function() { | |
290 var passkeyEl = $('bluetooth-pairing-passkey-display'); | |
291 var keyClass = this.device_.pairing == PAIRING.REMOTE_PIN_CODE ? | |
292 'bluetooth-keyboard-button' : 'bluetooth-passkey-char'; | |
293 this.clearElement_(passkeyEl); | |
294 var key = String(this.device_.pincode); | |
295 for (var i = 0; i < key.length; i++) { | |
296 var keyEl = document.createElement('span'); | |
297 keyEl.textContent = key.charAt(i); | |
298 keyEl.className = keyClass; | |
299 keyEl.classList.add('key-pin'); | |
300 passkeyEl.appendChild(keyEl); | |
301 } | |
302 if (this.device_.pairing == PAIRING.REMOTE_PIN_CODE) { | |
303 // Add enter key. | |
304 var label = loadTimeData.getString('bluetoothEnterKey'); | |
305 var keyEl = document.createElement('span'); | |
306 keyEl.textContent = label; | |
307 keyEl.className = keyClass; | |
308 keyEl.classList.add('key-pin'); | |
309 keyEl.id = 'bluetooth-enter-key'; | |
310 passkeyEl.appendChild(keyEl); | |
311 } | |
312 passkeyEl.hidden = false; | |
313 }, | |
314 }; | |
315 | |
316 /** | |
317 * Configures the device pairing instructions and displays the pairing | |
318 * overlay. | |
319 * @param {Object} device Description of the Bluetooth device. | |
320 */ | |
321 BluetoothPairing.showDialog = function(device) { | |
322 BluetoothPairing.getInstance().update(device); | |
323 OptionsPage.showPageByName('bluetoothPairing', false); | |
324 }; | |
325 | |
326 /** | |
327 * Displays a message from the Bluetooth adapter. | |
328 * @param {{string: label, | |
329 * string: address} data Data for constructing the message. | |
330 */ | |
331 BluetoothPairing.showMessage = function(data) { | |
332 var name = ''; | |
333 if (data.address.length > 0) { | |
334 name = data.address; | |
335 var list = $('bluetooth-paired-devices-list'); | |
336 var index = list.find(name); | |
337 if (index == undefined) { | |
338 list = $('bluetooth-unpaired-devices-list'); | |
339 index = list.find(name); | |
340 } | |
341 if (index != undefined) { | |
342 var entry = list.dataModel.item(index); | |
343 if (entry && entry.name) | |
344 name = entry.name; | |
345 } | |
346 } | |
347 BluetoothPairing.showDialog({name: name, | |
348 address: data.address, | |
349 pairing: data.label, | |
350 dismissible: false}); | |
351 }; | |
352 | |
353 /** | |
354 * Closes the Bluetooth pairing dialog. | |
355 */ | |
356 BluetoothPairing.dismissDialog = function() { | |
357 var overlay = OptionsPage.getTopmostVisiblePage(); | |
358 var dialog = BluetoothPairing.getInstance(); | |
359 if (overlay == dialog && dialog.dismissible_) | |
360 OptionsPage.closeOverlay(); | |
361 }; | |
362 | |
363 // Export | |
364 return { | |
365 BluetoothPairing: BluetoothPairing | |
366 }; | |
367 }); | |
OLD | NEW |