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