blob: 90b1d1757302c6fd1f0f37b2a9f55f43d206ff9d [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.
/**
* @typedef {{
* ConnectionState: string,
* iconURL: string,
* policyManaged: boolean,
* servicePath: string
* }}
* @see chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
*/
var NetworkInfo;
cr.define('options.network', function() {
var ArrayDataModel = cr.ui.ArrayDataModel;
var List = cr.ui.List;
var ListItem = cr.ui.ListItem;
var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
var Menu = cr.ui.Menu;
var MenuItem = cr.ui.MenuItem;
var ControlledSettingIndicator = options.ControlledSettingIndicator;
/**
* Network settings constants. These enums usually match their C++
* counterparts.
*/
function Constants() {}
// Cellular activation states:
Constants.ACTIVATION_STATE_UNKNOWN = 0;
Constants.ACTIVATION_STATE_ACTIVATED = 1;
Constants.ACTIVATION_STATE_ACTIVATING = 2;
Constants.ACTIVATION_STATE_NOT_ACTIVATED = 3;
Constants.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4;
/**
* Order in which controls are to appear in the network list sorted by key.
*/
Constants.NETWORK_ORDER = ['Ethernet',
'WiFi',
'WiMAX',
'Cellular',
'VPN',
'addConnection'];
/**
* ID of the menu that is currently visible.
* @type {?string}
* @private
*/
var activeMenu_ = null;
/**
* Indicates if cellular networks are available.
* @type {boolean}
* @private
*/
var cellularAvailable_ = false;
/**
* Indicates if cellular networks are enabled.
* @type {boolean}
* @private
*/
var cellularEnabled_ = false;
/**
* Indicates if cellular device supports network scanning.
* @type {boolean}
* @private
*/
var cellularSupportsScan_ = false;
/**
* Indicates the current SIM lock type of the cellular device.
* @type {string}
* @private
*/
var cellularSimLockType_ = '';
/**
* Indicates whether the SIM card is absent on the cellular device.
* @type {boolean}
* @private
*/
var cellularSimAbsent_ = false;
/**
* Indicates if WiMAX networks are available.
* @type {boolean}
* @private
*/
var wimaxAvailable_ = false;
/**
* Indicates if WiMAX networks are enabled.
* @type {boolean}
* @private
*/
var wimaxEnabled_ = false;
/**
* Indicates if mobile data roaming is enabled.
* @type {boolean}
* @private
*/
var enableDataRoaming_ = false;
/**
* Icon to use when not connected to a particular type of network.
* @type {!Object.<string, string>} Mapping of network type to icon data url.
* @private
*/
var defaultIcons_ = {};
/**
* Returns the display name for 'network'.
* @param {Object} data The network data dictionary.
*/
function getNetworkName(data) {
if (data.Type == 'Ethernet')
return loadTimeData.getString('ethernetName');
return data.Name;
}
/**
* Create an element in the network list for controlling network
* connectivity.
* @param {Object} data Description of the network list or command.
* @constructor
* @extends {cr.ui.ListItem}
*/
function NetworkListItem(data) {
var el = cr.doc.createElement('li');
el.data_ = {};
for (var key in data)
el.data_[key] = data[key];
NetworkListItem.decorate(el);
return el;
}
/**
* @param {string} action An action to send to coreOptionsUserMetricsAction.
*/
function sendChromeMetricsAction(action) {
chrome.send('coreOptionsUserMetricsAction', [action]);
}
/**
* Decorate an element as a NetworkListItem.
* @param {!Element} el The element to decorate.
*/
NetworkListItem.decorate = function(el) {
el.__proto__ = NetworkListItem.prototype;
el.decorate();
};
NetworkListItem.prototype = {
__proto__: ListItem.prototype,
/**
* Description of the network group or control.
* @type {Object.<string,Object>}
* @private
*/
data_: null,
/**
* Element for the control's subtitle.
* @type {?Element}
* @private
*/
subtitle_: null,
/**
* Icon for the network control.
* @type {?Element}
* @private
*/
icon_: null,
/**
* Indicates if in the process of connecting to a network.
* @type {boolean}
* @private
*/
connecting_: false,
/**
* Description of the network control.
* @type {Object}
*/
get data() {
return this.data_;
},
/**
* Text label for the subtitle.
* @type {string}
*/
set subtitle(text) {
if (text)
this.subtitle_.textContent = text;
this.subtitle_.hidden = !text;
},
/**
* URL for the network icon.
* @type {string}
*/
set iconURL(iconURL) {
this.icon_.style.backgroundImage = url(iconURL);
},
/**
* Type of network icon. Each type corresponds to a CSS rule.
* @type {string}
*/
set iconType(type) {
if (defaultIcons_[type])
this.iconURL = defaultIcons_[type];
else
this.icon_.classList.add('network-' + type.toLowerCase());
},
/**
* Indicates if the network is in the process of being connected.
* @type {boolean}
*/
set connecting(state) {
this.connecting_ = state;
if (state)
this.icon_.classList.add('network-connecting');
else
this.icon_.classList.remove('network-connecting');
},
/**
* Indicates if the network is in the process of being connected.
* @type {boolean}
*/
get connecting() {
return this.connecting_;
},
/**
* Set the direction of the text.
* @param {string} direction The direction of the text, e.g. 'ltr'.
*/
setSubtitleDirection: function(direction) {
this.subtitle_.dir = direction;
},
/**
* Indicate that the selector arrow should be shown.
*/
showSelector: function() {
this.subtitle_.classList.add('network-selector');
},
/**
* Adds an indicator to show that the network is policy managed.
*/
showManagedNetworkIndicator: function() {
this.appendChild(new ManagedNetworkIndicator());
},
/** @override */
decorate: function() {
ListItem.prototype.decorate.call(this);
this.className = 'network-group';
this.icon_ = this.ownerDocument.createElement('div');
this.icon_.className = 'network-icon';
this.appendChild(this.icon_);
var textContent = this.ownerDocument.createElement('div');
textContent.className = 'network-group-labels';
this.appendChild(textContent);
var categoryLabel = this.ownerDocument.createElement('div');
var title;
if (this.data_.key == 'addConnection')
title = 'addConnectionTitle';
else
title = this.data_.key.toLowerCase() + 'Title';
categoryLabel.className = 'network-title';
categoryLabel.textContent = loadTimeData.getString(title);
textContent.appendChild(categoryLabel);
this.subtitle_ = this.ownerDocument.createElement('div');
this.subtitle_.className = 'network-subtitle';
textContent.appendChild(this.subtitle_);
},
};
/**
* Creates a control that displays a popup menu when clicked.
* @param {Object} data Description of the control.
* @constructor
* @extends {NetworkListItem}
*/
function NetworkMenuItem(data) {
var el = new NetworkListItem(data);
el.__proto__ = NetworkMenuItem.prototype;
el.decorate();
return el;
}
NetworkMenuItem.prototype = {
__proto__: NetworkListItem.prototype,
/**
* Popup menu element.
* @type {?Element}
* @private
*/
menu_: null,
/** @override */
decorate: function() {
this.subtitle = null;
if (this.data.iconType)
this.iconType = this.data.iconType;
this.addEventListener('click', function() {
this.showMenu();
});
},
/**
* Retrieves the ID for the menu.
*/
getMenuName: function() {
return this.data_.key.toLowerCase() + '-network-menu';
},
/**
* Creates a popup menu for the control.
* @return {Element} The newly created menu.
*/
createMenu: function() {
if (this.data.menu) {
var menu = this.ownerDocument.createElement('div');
menu.id = this.getMenuName();
menu.className = 'network-menu';
menu.hidden = true;
Menu.decorate(menu);
for (var i = 0; i < this.data.menu.length; i++) {
var entry = this.data.menu[i];
createCallback_(menu, null, entry.label, entry.command);
}
return menu;
}
return null;
},
canUpdateMenu: function() {
return false;
},
/**
* Displays a popup menu.
*/
showMenu: function() {
var rebuild = false;
// Force a rescan if opening the menu for WiFi networks to ensure the
// list is up to date. Networks are periodically rescanned, but depending
// on timing, there could be an excessive delay before the first rescan
// unless forced.
var rescan = !activeMenu_ && this.data_.key == 'WiFi';
if (!this.menu_) {
rebuild = true;
var existing = $(this.getMenuName());
if (existing) {
if (this.updateMenu())
return;
closeMenu_();
}
this.menu_ = this.createMenu();
this.menu_.addEventListener('mousedown', function(e) {
// Prevent blurring of list, which would close the menu.
e.preventDefault();
});
var parent = $('network-menus');
if (existing)
parent.replaceChild(this.menu_, existing);
else
parent.appendChild(this.menu_);
}
var top = this.offsetTop + this.clientHeight;
var menuId = this.getMenuName();
if (menuId != activeMenu_ || rebuild) {
closeMenu_();
activeMenu_ = menuId;
this.menu_.style.setProperty('top', top + 'px');
this.menu_.hidden = false;
}
if (rescan) {
// TODO(stevenjb): chrome.networkingPrivate.requestNetworkScan
chrome.send('requestNetworkScan');
}
}
};
/**
* Creates a control for selecting or configuring a network connection based
* on the type of connection (e.g. wifi versus vpn).
* @param {{key: string, networkList: Array.<NetworkInfo>}} data Description
* of the network.
* @constructor
* @extends {NetworkMenuItem}
*/
function NetworkSelectorItem(data) {
var el = new NetworkMenuItem(data);
el.__proto__ = NetworkSelectorItem.prototype;
el.decorate();
return el;
}
NetworkSelectorItem.prototype = {
__proto__: NetworkMenuItem.prototype,
/** @override */
decorate: function() {
// TODO(kevers): Generalize method of setting default label.
var policyManaged = false;
this.subtitle = loadTimeData.getString('OncConnectionStateNotConnected');
var list = this.data_.networkList;
var candidateURL = null;
for (var i = 0; i < list.length; i++) {
var networkDetails = list[i];
if (networkDetails.ConnectionState == 'Connecting' ||
networkDetails.ConnectionState == 'Connected') {
this.subtitle = getNetworkName(networkDetails);
this.setSubtitleDirection('ltr');
policyManaged = networkDetails.policyManaged;
candidateURL = networkDetails.iconURL;
// Only break when we see a connecting network as it is possible to
// have a connected network and a connecting network at the same
// time.
if (networkDetails.ConnectionState == 'Connecting') {
this.connecting = true;
candidateURL = null;
break;
}
}
}
if (candidateURL)
this.iconURL = candidateURL;
else
this.iconType = this.data.key;
this.showSelector();
if (policyManaged)
this.showManagedNetworkIndicator();
if (activeMenu_ == this.getMenuName()) {
// Menu is already showing and needs to be updated. Explicitly calling
// show menu will force the existing menu to be replaced. The call
// is deferred in order to ensure that position of this element has
// beem properly updated.
var self = this;
setTimeout(function() {self.showMenu();}, 0);
}
},
/**
* Creates a menu for selecting, configuring or disconnecting from a
* network.
* @return {!Element} The newly created menu.
*/
createMenu: function() {
var menu = this.ownerDocument.createElement('div');
menu.id = this.getMenuName();
menu.className = 'network-menu';
menu.hidden = true;
Menu.decorate(menu);
var addendum = [];
if (this.data_.key == 'WiFi') {
addendum.push({
label: loadTimeData.getString('joinOtherNetwork'),
command: createAddConnectionCallback_('WiFi'),
data: {}
});
} else if (this.data_.key == 'Cellular') {
if (cellularEnabled_ && cellularSupportsScan_) {
addendum.push({
label: loadTimeData.getString('otherCellularNetworks'),
command: createAddConnectionCallback_('Cellular'),
addClass: ['other-cellulars'],
data: {}
});
}
var label = enableDataRoaming_ ? 'disableDataRoaming' :
'enableDataRoaming';
var disabled = !loadTimeData.getValue('loggedInAsOwner');
var entry = {label: loadTimeData.getString(label),
data: {}};
if (disabled) {
entry.command = null;
entry.tooltip =
loadTimeData.getString('dataRoamingDisableToggleTooltip');
} else {
var self = this;
entry.command = function() {
options.Preferences.setBooleanPref(
'cros.signed.data_roaming_enabled',
!enableDataRoaming_, true);
// Force revalidation of the menu the next time it is displayed.
self.menu_ = null;
};
}
addendum.push(entry);
} else if (this.data_.key == 'VPN') {
addendum.push({
label: loadTimeData.getString('joinOtherNetwork'),
command: createAddConnectionCallback_('VPN'),
data: {}
});
}
var list = this.data.rememberedNetworks;
if (list && list.length > 0) {
var callback = function(list) {
$('remembered-network-list').clear();
var dialog = options.PreferredNetworks.getInstance();
PageManager.showPageByName('preferredNetworksPage', false);
dialog.update(list);
sendChromeMetricsAction('Options_NetworkShowPreferred');
};
addendum.push({label: loadTimeData.getString('preferredNetworks'),
command: callback,
data: list});
}
var networkGroup = this.ownerDocument.createElement('div');
networkGroup.className = 'network-menu-group';
list = this.data.networkList;
var empty = !list || list.length == 0;
if (list) {
var connectedVpnServicePath = '';
for (var i = 0; i < list.length; i++) {
var data = list[i];
this.createNetworkOptionsCallback_(networkGroup, data);
// For VPN only, append a 'Disconnect' item to the dropdown menu.
if (!connectedVpnServicePath && data.Type == 'VPN' &&
(data.ConnectionState == 'Connected' ||
data.ConnectionState == 'Connecting')) {
connectedVpnServicePath = data.servicePath;
}
}
if (connectedVpnServicePath) {
var disconnectCallback = function() {
sendChromeMetricsAction('Options_NetworkDisconnectVPN');
// TODO(stevenjb): chrome.networkingPrivate.startDisconnect
chrome.send('startDisconnect', [connectedVpnServicePath]);
};
// Add separator
addendum.push({});
addendum.push({label: loadTimeData.getString('disconnectNetwork'),
command: disconnectCallback,
data: data});
}
}
if (this.data_.key == 'WiFi' || this.data_.key == 'WiMAX' ||
this.data_.key == 'Cellular') {
addendum.push({});
if (this.data_.key == 'WiFi') {
addendum.push({
label: loadTimeData.getString('turnOffWifi'),
command: function() {
sendChromeMetricsAction('Options_NetworkWifiToggle');
// TODO(stevenjb): chrome.networkingPrivate.disableNetworkType
chrome.send('disableNetworkType', ['WiFi']);
},
data: {}});
} else if (this.data_.key == 'WiMAX') {
addendum.push({
label: loadTimeData.getString('turnOffWimax'),
command: function() {
// TODO(stevenjb): chrome.networkingPrivate.disableNetworkType
chrome.send('disableNetworkType', ['WiMAX']);
},
data: {}});
} else if (this.data_.key == 'Cellular') {
addendum.push({
label: loadTimeData.getString('turnOffCellular'),
command: function() {
// TODO(stevenjb): chrome.networkingPrivate.disableNetworkType
chrome.send('disableNetworkType', ['Cellular']);
},
data: {}});
}
}
if (!empty)
menu.appendChild(networkGroup);
if (addendum.length > 0) {
var separator = false;
if (!empty) {
menu.appendChild(MenuItem.createSeparator());
separator = true;
}
for (var i = 0; i < addendum.length; i++) {
var value = addendum[i];
if (value.data) {
var item = createCallback_(menu, value.data, value.label,
value.command);
if (value.tooltip)
item.title = value.tooltip;
if (value.addClass)
item.classList.add(value.addClass);
separator = false;
} else if (!separator) {
menu.appendChild(MenuItem.createSeparator());
separator = true;
}
}
}
return menu;
},
/**
* Determines if a menu can be updated on the fly. Menus that cannot be
* updated are fully regenerated using createMenu. The advantage of
* updating a menu is that it can preserve ordering of networks avoiding
* entries from jumping around after an update.
*/
canUpdateMenu: function() {
return this.data_.key == 'WiFi' && activeMenu_ == this.getMenuName();
},
/**
* Updates an existing menu. Updated menus preserve ordering of prior
* entries. During the update process, the ordering may differ from the
* preferred ordering as determined by the network library. If the
* ordering becomes potentially out of sync, then the updated menu is
* marked for disposal on close. Reopening the menu will force a
* regeneration, which will in turn fix the ordering.
* @return {boolean} True if successfully updated.
*/
updateMenu: function() {
if (!this.canUpdateMenu())
return false;
var oldMenu = $(this.getMenuName());
var group = oldMenu.getElementsByClassName('network-menu-group')[0];
if (!group)
return false;
var newMenu = this.createMenu();
var discardOnClose = false;
var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu);
var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu);
for (var key in oldNetworkButtons) {
if (newNetworkButtons[key]) {
group.replaceChild(newNetworkButtons[key].button,
oldNetworkButtons[key].button);
if (newNetworkButtons[key].index != oldNetworkButtons[key].index)
discardOnClose = true;
newNetworkButtons[key] = null;
} else {
// Leave item in list to prevent network items from jumping due to
// deletions.
oldNetworkButtons[key].disabled = true;
discardOnClose = true;
}
}
for (var key in newNetworkButtons) {
var entry = newNetworkButtons[key];
if (entry) {
group.appendChild(entry.button);
discardOnClose = true;
}
}
oldMenu.data = {discardOnClose: discardOnClose};
return true;
},
/**
* Extracts a mapping of network names to menu element and position.
* @param {!Element} menu The menu to process.
* @return {Object.<string, ?{index: number, button: Element}>}
* Network mapping.
* @private
*/
extractNetworkConnectButtons_: function(menu) {
var group = menu.getElementsByClassName('network-menu-group')[0];
var networkButtons = {};
if (!group)
return networkButtons;
var buttons = group.getElementsByClassName('network-menu-item');
for (var i = 0; i < buttons.length; i++) {
var label = buttons[i].data.label;
networkButtons[label] = {index: i, button: buttons[i]};
}
return networkButtons;
},
/**
* Adds a menu item for showing network details.
* @param {!Element} parent The parent element.
* @param {Object} data Description of the network.
* @private
*/
createNetworkOptionsCallback_: function(parent, data) {
var menuItem = createCallback_(parent,
data,
getNetworkName(data),
'showDetails',
data.iconURL);
if (data.policyManaged)
menuItem.appendChild(new ManagedNetworkIndicator());
if (data.ConnectionState == 'Connected' ||
data.ConnectionState == 'Connecting') {
var label = menuItem.getElementsByClassName(
'network-menu-item-label')[0];
label.classList.add('active-network');
}
}
};
/**
* Creates a button-like control for configurating internet connectivity.
* @param {{key: string, subtitle: string, command: Function}} data
* Description of the network control.
* @constructor
* @extends {NetworkListItem}
*/
function NetworkButtonItem(data) {
var el = new NetworkListItem(data);
el.__proto__ = NetworkButtonItem.prototype;
el.decorate();
return el;
}
NetworkButtonItem.prototype = {
__proto__: NetworkListItem.prototype,
/** @override */
decorate: function() {
if (this.data.subtitle)
this.subtitle = this.data.subtitle;
else
this.subtitle = null;
if (this.data.command)
this.addEventListener('click', this.data.command);
if (this.data.iconURL)
this.iconURL = this.data.iconURL;
else if (this.data.iconType)
this.iconType = this.data.iconType;
if (this.data.policyManaged)
this.showManagedNetworkIndicator();
},
};
/**
* Adds a command to a menu for modifying network settings.
* @param {!Element} menu Parent menu.
* @param {Object} data Description of the network.
* @param {!string} label Display name for the menu item.
* @param {?(string|!Function)} command Callback function or name
* of the command for |networkCommand|.
* @param {string=} opt_iconURL Optional URL to an icon for the menu item.
* @return {!Element} The created menu item.
* @private
*/
function createCallback_(menu, data, label, command, opt_iconURL) {
var button = menu.ownerDocument.createElement('div');
button.className = 'network-menu-item';
var buttonIcon = menu.ownerDocument.createElement('div');
buttonIcon.className = 'network-menu-item-icon';
button.appendChild(buttonIcon);
if (opt_iconURL)
buttonIcon.style.backgroundImage = url(opt_iconURL);
var buttonLabel = menu.ownerDocument.createElement('span');
buttonLabel.className = 'network-menu-item-label';
buttonLabel.textContent = label;
button.appendChild(buttonLabel);
var callback = null;
if (typeof command == 'string') {
var type = data.Type;
var path = data.servicePath;
callback = function() {
chrome.send('networkCommand', [type, path, command]);
closeMenu_();
};
} else if (command != null) {
if (data) {
callback = function() {
(/** @type {Function} */(command))(data);
closeMenu_();
};
} else {
callback = function() {
(/** @type {Function} */(command))();
closeMenu_();
};
}
}
if (callback != null)
button.addEventListener('click', callback);
else
buttonLabel.classList.add('network-disabled-control');
button.data = {label: label};
MenuItem.decorate(button);
menu.appendChild(button);
return button;
}
/**
* A list of controls for manipulating network connectivity.
* @constructor
* @extends {cr.ui.List}
*/
var NetworkList = cr.ui.define('list');
NetworkList.prototype = {
__proto__: List.prototype,
/** @override */
decorate: function() {
List.prototype.decorate.call(this);
this.startBatchUpdates();
this.autoExpands = true;
this.dataModel = new ArrayDataModel([]);
this.selectionModel = new ListSingleSelectionModel();
this.addEventListener('blur', this.onBlur_.bind(this));
this.selectionModel.addEventListener('change',
this.onSelectionChange_.bind(this));
// Wi-Fi control is always visible.
this.update({key: 'WiFi', networkList: []});
var entryAddWifi = {
label: loadTimeData.getString('addConnectionWifi'),
command: createAddConnectionCallback_('WiFi')
};
var entryAddVPN = {
label: loadTimeData.getString('addConnectionVPN'),
command: createAddConnectionCallback_('VPN')
};
this.update({key: 'addConnection',
iconType: 'add-connection',
menu: [entryAddWifi, entryAddVPN]
});
var prefs = options.Preferences.getInstance();
prefs.addEventListener('cros.signed.data_roaming_enabled',
function(event) {
enableDataRoaming_ = event.value.value;
});
this.endBatchUpdates();
},
/**
* When the list loses focus, unselect all items in the list and close the
* active menu.
* @private
*/
onBlur_: function() {
this.selectionModel.unselectAll();
closeMenu_();
},
/**
* Close bubble and menu when a different list item is selected.
* @param {Event} event Event detailing the selection change.
* @private
*/
onSelectionChange_: function(event) {
PageManager.hideBubble();
// A list item may temporarily become unselected while it is constructing
// its menu. The menu should therefore only be closed if a different item
// is selected, not when the menu's owner item is deselected.
if (activeMenu_) {
for (var i = 0; i < event.changes.length; ++i) {
if (event.changes[i].selected) {
var item = this.dataModel.item(event.changes[i].index);
if (!item.getMenuName || item.getMenuName() != activeMenu_) {
closeMenu_();
return;
}
}
}
}
},
/**
* Finds the index of a network item within the data model based on
* category.
* @param {string} key Unique key for the item in the list.
* @return {(number|undefined)} The index of the network item, or
* |undefined| if it is not found.
*/
indexOf: function(key) {
var size = this.dataModel.length;
for (var i = 0; i < size; i++) {
var entry = this.dataModel.item(i);
if (entry.key == key)
return i;
}
return undefined;
},
/**
* Updates a network control.
* @param {Object.<string,string>} data Description of the entry.
*/
update: function(data) {
this.startBatchUpdates();
var index = this.indexOf(data.key);
if (index == undefined) {
// Find reference position for adding the element. We cannot hide
// individual list elements, thus we need to conditionally add or
// remove elements and cannot rely on any element having a fixed index.
for (var i = 0; i < Constants.NETWORK_ORDER.length; i++) {
if (data.key == Constants.NETWORK_ORDER[i]) {
data.sortIndex = i;
break;
}
}
var referenceIndex = -1;
for (var i = 0; i < this.dataModel.length; i++) {
var entry = this.dataModel.item(i);
if (entry.sortIndex < data.sortIndex)
referenceIndex = i;
else
break;
}
if (referenceIndex == -1) {
// Prepend to the start of the list.
this.dataModel.splice(0, 0, data);
} else if (referenceIndex == this.dataModel.length) {
// Append to the end of the list.
this.dataModel.push(data);
} else {
// Insert after the reference element.
this.dataModel.splice(referenceIndex + 1, 0, data);
}
} else {
var entry = this.dataModel.item(index);
data.sortIndex = entry.sortIndex;
this.dataModel.splice(index, 1, data);
}
this.endBatchUpdates();
},
/**
* @override
* @param {Object} entry
*/
createItem: function(entry) {
if (entry.networkList)
return new NetworkSelectorItem(
/** @type {{key: string, networkList: Array.<NetworkInfo>}} */(
entry));
if (entry.command)
return new NetworkButtonItem(
/** @type {{key: string, subtitle: string, command: Function}} */(
entry));
if (entry.menu)
return new NetworkMenuItem(entry);
assertNotReached();
},
/**
* Deletes an element from the list.
* @param {string} key Unique identifier for the element.
*/
deleteItem: function(key) {
var index = this.indexOf(key);
if (index != undefined)
this.dataModel.splice(index, 1);
},
/**
* Updates the state of a toggle button.
* @param {string} key Unique identifier for the element.
* @param {boolean} active Whether the control is active.
*/
updateToggleControl: function(key, active) {
var index = this.indexOf(key);
if (index != undefined) {
var entry = this.dataModel.item(index);
entry.iconType = active ? 'control-active' :
'control-inactive';
this.update(entry);
}
}
};
/**
* Sets the default icon to use for each network type if disconnected.
* @param {!Object.<string, string>} data Mapping of network type to icon
* data url.
*/
NetworkList.setDefaultNetworkIcons = function(data) {
defaultIcons_ = Object.create(data);
};
/**
* Chrome callback for updating network controls.
* @param {{cellularAvailable: boolean,
* cellularEnabled: boolean,
* cellularSimAbsent: boolean,
* cellularSimLockType: string,
* cellularSupportsScan: boolean,
* rememberedList: Array.<NetworkInfo>,
* vpnList: Array.<NetworkInfo>,
* wifiAvailable: boolean,
* wifiEnabled: boolean,
* wimaxAvailable: boolean,
* wimaxEnabled: boolean,
* wiredList: Array.<NetworkInfo>,
* wirelessList: Array.<NetworkInfo>}} data Description of available
* network devices and their corresponding state.
*/
NetworkList.refreshNetworkData = function(data) {
var networkList = $('network-list');
networkList.startBatchUpdates();
cellularAvailable_ = data.cellularAvailable;
cellularEnabled_ = data.cellularEnabled;
cellularSupportsScan_ = data.cellularSupportsScan;
cellularSimAbsent_ = data.cellularSimAbsent;
cellularSimLockType_ = data.cellularSimLockType;
wimaxAvailable_ = data.wimaxAvailable;
wimaxEnabled_ = data.wimaxEnabled;
// Only show Ethernet control if connected.
var ethernetConnection = getConnection_(data.wiredList);
if (ethernetConnection) {
var type = String('Ethernet');
var path = ethernetConnection.servicePath;
var ethernetOptions = function() {
chrome.send('networkCommand', [type, path, 'showDetails']);
};
networkList.update(
{ key: 'Ethernet',
subtitle: loadTimeData.getString('OncConnectionStateConnected'),
iconURL: ethernetConnection.iconURL,
command: ethernetOptions,
policyManaged: ethernetConnection.policyManaged }
);
} else {
networkList.deleteItem('Ethernet');
}
if (data.wifiEnabled)
loadData_('WiFi', data.wirelessList, data.rememberedList);
else
addEnableNetworkButton_('WiFi');
// Only show cellular control if available.
if (data.cellularAvailable) {
if (data.cellularEnabled)
loadData_('Cellular', data.wirelessList, data.rememberedList);
else
addEnableNetworkButton_('Cellular');
} else {
networkList.deleteItem('Cellular');
}
// Only show wimax control if available. Uses cellular icons.
if (data.wimaxAvailable) {
if (data.wimaxEnabled)
loadData_('WiMAX', data.wirelessList, data.rememberedList);
else
addEnableNetworkButton_('WiMAX');
} else {
networkList.deleteItem('WiMAX');
}
// Only show VPN control if there is at least one VPN configured.
if (data.vpnList.length > 0)
loadData_('VPN', data.vpnList, data.rememberedList);
else
networkList.deleteItem('VPN');
networkList.endBatchUpdates();
};
/**
* Replaces a network menu with a button for enabling the network type.
* @param {string} type The type of network (WiFi, Cellular or Wimax).
* @private
*/
function addEnableNetworkButton_(type) {
var subtitle = loadTimeData.getString('networkDisabled');
var icon = (type == 'WiMAX') ? 'Cellular' : type;
var enableNetwork = function() {
if (type == 'WiFi')
sendChromeMetricsAction('Options_NetworkWifiToggle');
if (type == 'Cellular') {
if (cellularSimLockType_) {
chrome.send('simOperation', ['unlock']);
return;
} else if (cellularEnabled_ && cellularSimAbsent_) {
chrome.send('simOperation', ['configure']);
return;
}
}
// TODO(stevenjb): chrome.networkingPrivate.enableNetworkType
chrome.send('enableNetworkType', [type]);
};
$('network-list').update({key: type,
subtitle: subtitle,
iconType: icon,
command: enableNetwork});
}
/**
* Element for indicating a policy managed network.
* @constructor
* @extends {options.ControlledSettingIndicator}
*/
function ManagedNetworkIndicator() {
var el = cr.doc.createElement('span');
el.__proto__ = ManagedNetworkIndicator.prototype;
el.decorate();
return el;
}
ManagedNetworkIndicator.prototype = {
__proto__: ControlledSettingIndicator.prototype,
/** @override */
decorate: function() {
ControlledSettingIndicator.prototype.decorate.call(this);
this.controlledBy = 'policy';
var policyLabel = loadTimeData.getString('managedNetwork');
this.setAttribute('textPolicy', policyLabel);
this.removeAttribute('tabindex');
},
/** @override */
handleEvent: function(event) {
// Prevent focus blurring as that would close any currently open menu.
if (event.type == 'mousedown')
return;
ControlledSettingIndicator.prototype.handleEvent.call(this, event);
},
/**
* Handle mouse events received by the bubble, preventing focus blurring as
* that would close any currently open menu and preventing propagation to
* any elements located behind the bubble.
* @param {Event} event Mouse event.
*/
stopEvent: function(event) {
event.preventDefault();
event.stopPropagation();
},
/** @override */
toggleBubble: function() {
if (activeMenu_ && !$(activeMenu_).contains(this))
closeMenu_();
ControlledSettingIndicator.prototype.toggleBubble.call(this);
if (this.showingBubble) {
var bubble = PageManager.getVisibleBubble();
bubble.addEventListener('mousedown', this.stopEvent);
bubble.addEventListener('click', this.stopEvent);
}
}
};
/**
* Updates the list of available networks and their status, filtered by
* network type.
* @param {string} type The type of network.
* @param {Array} available The list of available networks and their status.
* @param {Array} remembered The list of remmebered networks.
*/
function loadData_(type, available, remembered) {
var data = {key: type};
var availableNetworks = [];
for (var i = 0; i < available.length; i++) {
if (available[i].Type == type)
availableNetworks.push(available[i]);
}
data.networkList = availableNetworks;
if (remembered) {
var rememberedNetworks = [];
for (var i = 0; i < remembered.length; i++) {
if (remembered[i].Type == type)
rememberedNetworks.push(remembered[i]);
}
data.rememberedNetworks = rememberedNetworks;
}
$('network-list').update(data);
}
/**
* Hides the currently visible menu.
* @private
*/
function closeMenu_() {
if (activeMenu_) {
var menu = $(activeMenu_);
menu.hidden = true;
if (menu.data && menu.data.discardOnClose)
menu.parentNode.removeChild(menu);
activeMenu_ = null;
}
}
/**
* Fetches the active connection.
* @param {Array.<Object>} networkList List of networks.
* @return {Object}
* @private
*/
function getConnection_(networkList) {
if (!networkList)
return null;
for (var i = 0; i < networkList.length; i++) {
var entry = networkList[i];
if (entry.ConnectionState == 'Connected' ||
entry.ConnectionState == 'Connecting')
return entry;
}
return null;
}
/**
* Create a callback function that adds a new connection of the given type.
* @param {string} type An ONC network type
* @return {function()} The created callback.
* @private
*/
function createAddConnectionCallback_(type) {
return function() {
if (type == 'WiFi')
sendChromeMetricsAction('Options_NetworkJoinOtherWifi');
else if (type == 'VPN')
sendChromeMetricsAction('Options_NetworkJoinOtherVPN');
chrome.send('networkCommand', [type, '', 'add']);
};
}
/**
* Whether the Network list is disabled. Only used for display purpose.
*/
cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR);
// Export
return {
NetworkList: NetworkList
};
});