blob: be86dfc1ac60eb0026f6ac313da0c191b566bdbe [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.define('options', function() {
/** @const */ var OptionsPage = options.OptionsPage;
/**
* Enumeration of possible states during pairing. The value associated with
* each state maps to a localized string in the global variable
* |loadTimeData|.
* @enum {string}
*/
var PAIRING = {
STARTUP: 'bluetoothStartConnecting',
ENTER_PIN_CODE: 'bluetoothEnterPinCode',
ENTER_PASSKEY: 'bluetoothEnterPasskey',
REMOTE_PIN_CODE: 'bluetoothRemotePinCode',
REMOTE_PASSKEY: 'bluetoothRemotePasskey',
CONFIRM_PASSKEY: 'bluetoothConfirmPasskey',
};
/**
* List of IDs for conditionally visible elements in the dialog.
* @type {Array.<String>}
* @const
*/
var ELEMENTS = ['bluetooth-pairing-passkey-display',
'bluetooth-pairing-passkey-entry',
'bluetooth-pairing-pincode-entry',
'bluetooth-pair-device-connect-button',
'bluetooth-pair-device-cancel-button',
'bluetooth-pair-device-accept-button',
'bluetooth-pair-device-reject-button',
'bluetooth-pair-device-dismiss-button'];
/**
* Encapsulated handling of the Bluetooth device pairing page.
* @constructor
*/
function BluetoothPairing() {
OptionsPage.call(this,
'bluetoothPairing',
loadTimeData.getString('bluetoothOptionsPageTabTitle'),
'bluetooth-pairing');
}
cr.addSingletonGetter(BluetoothPairing);
BluetoothPairing.prototype = {
__proto__: OptionsPage.prototype,
/**
* Description of the bluetooth device.
* @type {{name: string,
* address: string,
* icon: Constants.DEVICE_TYPE,
* paired: boolean,
* bonded: boolean,
* connected: boolean,
* pairing: string|undefined,
* passkey: number|undefined,
* pincode: string|undefined,
* entered: number|undefined}}
* @private.
*/
device_: null,
/**
* Can the dialog be programmatically dismissed.
* @type {boolean}
*/
dismissible_: true,
/** @inheritDoc */
initializePage: function() {
OptionsPage.prototype.initializePage.call(this);
var self = this;
$('bluetooth-pair-device-cancel-button').onclick = function() {
chrome.send('updateBluetoothDevice',
[self.device_.address, 'cancel']);
OptionsPage.closeOverlay();
};
$('bluetooth-pair-device-reject-button').onclick = function() {
chrome.send('updateBluetoothDevice',
[self.device_.address, 'reject']);
OptionsPage.closeOverlay();
};
$('bluetooth-pair-device-connect-button').onclick = function() {
var args = [self.device_.address, 'connect'];
var passkey = self.device_.passkey;
if (passkey)
args.push(String(passkey));
else if (!$('bluetooth-pairing-passkey-entry').hidden)
args.push($('bluetooth-passkey').value);
else if (!$('bluetooth-pairing-pincode-entry').hidden)
args.push($('bluetooth-pincode').value);
chrome.send('updateBluetoothDevice', args);
OptionsPage.closeOverlay();
};
$('bluetooth-pair-device-accept-button').onclick = function() {
chrome.send('updateBluetoothDevice',
[self.device_.address, 'accept']);
OptionsPage.closeOverlay();
};
$('bluetooth-pair-device-dismiss-button').onclick = function() {
OptionsPage.closeOverlay();
};
$('bluetooth-passkey').oninput = function() {
var inputField = $('bluetooth-passkey');
var value = inputField.value;
// Note that using <input type="number"> is insufficient to restrict
// the input as it allows negative numbers and does not limit the
// number of charactes typed even if a range is set. Furthermore,
// it sometimes produces strange repaint artifacts.
var filtered = value.replace(/[^0-9]/g, '');
if (filtered != value)
inputField.value = filtered;
$('bluetooth-pair-device-connect-button').disabled =
inputField.value.length == 0;
}
$('bluetooth-pincode').oninput = function() {
$('bluetooth-pair-device-connect-button').disabled =
$('bluetooth-pincode').value.length == 0;
}
$('bluetooth-passkey').addEventListener('keydown',
this.keyDownEventHandler_.bind(this));
$('bluetooth-pincode').addEventListener('keydown',
this.keyDownEventHandler_.bind(this));
},
/**
* Override to prevent showing the overlay if the Bluetooth device details
* have not been specified. Prevents showing an empty dialog if the user
* quits and restarts Chrome while in the process of pairing with a device.
* @return {boolean} True if the overlay can be displayed.
*/
canShowPage: function() {
return this.device_ && this.device_.address && this.device_.pairing;
},
/**
* Sets input focus on the passkey or pincode field if appropriate.
*/
didShowPage: function() {
if (!$('bluetooth-pincode').hidden)
$('bluetooth-pincode').focus();
else if (!$('bluetooth-passkey').hidden)
$('bluetooth-passkey').focus();
},
/**
* Configures the overlay for pairing a device.
* @param {Object} device Description of the bluetooth device.
*/
update: function(device) {
this.device_ = {};
for (key in device)
this.device_[key] = device[key];
// Update the pairing instructions.
var instructionsEl = $('bluetooth-pairing-instructions');
this.clearElement_(instructionsEl);
this.dismissible_ = ('dismissible' in device) ?
device.dismissible : true;
var message = loadTimeData.getString(device.pairing);
message = message.replace('%1', this.device_.name);
instructionsEl.textContent = message;
// Update visibility of dialog elements.
if (this.device_.passkey) {
this.updatePasskey_();
if (this.device_.pairing == PAIRING.CONFIRM_PASSKEY) {
// Confirming a match between displayed passkeys.
this.displayElements_(['bluetooth-pairing-passkey-display',
'bluetooth-pair-device-accept-button',
'bluetooth-pair-device-reject-button']);
} else {
// Remote entering a passkey.
this.displayElements_(['bluetooth-pairing-passkey-display',
'bluetooth-pair-device-cancel-button']);
}
} else if (this.device_.pincode) {
this.updatePinCode_();
this.displayElements_(['bluetooth-pairing-passkey-display',
'bluetooth-pair-device-cancel-button']);
} else if (this.device_.pairing == PAIRING.ENTER_PIN_CODE) {
// Prompting the user to enter a PIN code.
this.displayElements_(['bluetooth-pairing-pincode-entry',
'bluetooth-pair-device-connect-button',
'bluetooth-pair-device-cancel-button']);
$('bluetooth-pincode').value = '';
} else if (this.device_.pairing == PAIRING.ENTER_PASSKEY) {
// Prompting the user to enter a passkey.
this.displayElements_(['bluetooth-pairing-passkey-entry',
'bluetooth-pair-device-connect-button',
'bluetooth-pair-device-cancel-button']);
$('bluetooth-passkey').value = '';
} else if (this.device_.pairing == PAIRING.STARTUP) {
// Starting the pairing process.
this.displayElements_(['bluetooth-pair-device-cancel-button']);
} else {
// Displaying an error message.
this.displayElements_(['bluetooth-pair-device-dismiss-button']);
}
// User is required to enter a passkey or pincode before the connect
// button can be enabled. The 'oninput' methods for the input fields
// determine when the connect button becomes active.
$('bluetooth-pair-device-connect-button').disabled = true;
},
/**
* Handles the ENTER key for the passkey or pincode entry field.
* @return {Event} a keydown event.
* @private
*/
keyDownEventHandler_: function(event) {
/** @const */ var ENTER_KEY_CODE = 13;
if (event.keyCode == ENTER_KEY_CODE) {
var button = $('bluetooth-pair-device-connect-button');
if (!button.hidden)
button.click();
}
},
/**
* Updates the visibility of elements in the dialog.
* @param {Array.<string>} list List of conditionally visible elements that
* are to be made visible.
* @private
*/
displayElements_: function(list) {
var enabled = {};
for (var i = 0; i < list.length; i++) {
var key = list[i];
enabled[key] = true;
}
for (var i = 0; i < ELEMENTS.length; i++) {
var key = ELEMENTS[i];
$(key).hidden = !enabled[key];
}
},
/**
* Removes all children from an element.
* @param {!Element} element Target element to clear.
*/
clearElement_: function(element) {
var child = element.firstChild;
while (child) {
element.removeChild(child);
child = element.firstChild;
}
},
/**
* Formats an element for displaying the passkey.
*/
updatePasskey_: function() {
var passkeyEl = $('bluetooth-pairing-passkey-display');
var keyClass = this.device_.pairing == PAIRING.REMOTE_PASSKEY ?
'bluetooth-keyboard-button' : 'bluetooth-passkey-char';
this.clearElement_(passkeyEl);
var key = String(this.device_.passkey);
var progress = this.device_.entered | 0;
for (var i = 0; i < key.length; i++) {
var keyEl = document.createElement('span');
keyEl.textContent = key.charAt(i);
keyEl.className = keyClass;
if (i < progress)
keyEl.classList.add('key-typed');
passkeyEl.appendChild(keyEl);
}
if (this.device_.pairing == PAIRING.REMOTE_PASSKEY) {
// Add enter key.
var label = loadTimeData.getString('bluetoothEnterKey');
var keyEl = document.createElement('span');
keyEl.textContent = label;
keyEl.className = keyClass;
keyEl.id = 'bluetooth-enter-key';
passkeyEl.appendChild(keyEl);
}
passkeyEl.hidden = false;
},
/**
* Formats an element for displaying the PIN code.
*/
updatePinCode_: function() {
var passkeyEl = $('bluetooth-pairing-passkey-display');
var keyClass = this.device_.pairing == PAIRING.REMOTE_PIN_CODE ?
'bluetooth-keyboard-button' : 'bluetooth-passkey-char';
this.clearElement_(passkeyEl);
var key = String(this.device_.pincode);
for (var i = 0; i < key.length; i++) {
var keyEl = document.createElement('span');
keyEl.textContent = key.charAt(i);
keyEl.className = keyClass;
keyEl.classList.add('key-pin');
passkeyEl.appendChild(keyEl);
}
if (this.device_.pairing == PAIRING.REMOTE_PIN_CODE) {
// Add enter key.
var label = loadTimeData.getString('bluetoothEnterKey');
var keyEl = document.createElement('span');
keyEl.textContent = label;
keyEl.className = keyClass;
keyEl.classList.add('key-pin');
keyEl.id = 'bluetooth-enter-key';
passkeyEl.appendChild(keyEl);
}
passkeyEl.hidden = false;
},
};
/**
* Configures the device pairing instructions and displays the pairing
* overlay.
* @param {Object} device Description of the Bluetooth device.
*/
BluetoothPairing.showDialog = function(device) {
BluetoothPairing.getInstance().update(device);
OptionsPage.showPageByName('bluetoothPairing', false);
};
/**
* Displays a message from the Bluetooth adapter.
* @param {{string: label,
* string: address} data Data for constructing the message.
*/
BluetoothPairing.showMessage = function(data) {
var name = '';
if (data.address.length > 0) {
name = data.address;
var list = $('bluetooth-paired-devices-list');
var index = list.find(name);
if (index == undefined) {
list = $('bluetooth-unpaired-devices-list');
index = list.find(name);
}
if (index != undefined) {
var entry = list.dataModel.item(index);
if (entry && entry.name)
name = entry.name;
}
}
BluetoothPairing.showDialog({name: name,
address: data.address,
pairing: data.label,
dismissible: false});
};
/**
* Closes the Bluetooth pairing dialog.
*/
BluetoothPairing.dismissDialog = function() {
var overlay = OptionsPage.getTopmostVisiblePage();
var dialog = BluetoothPairing.getInstance();
if (overlay == dialog && dialog.dismissible_)
OptionsPage.closeOverlay();
};
// Export
return {
BluetoothPairing: BluetoothPairing
};
});