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