Move Webstore URL concepts to //extensions and out
[chromium-blink-merge.git] / chrome / browser / resources / options / chromeos / network_list.js
blob2d629667ce7bb7200350854cf41630735dc729fe
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.network', function() {
6 var ArrayDataModel = cr.ui.ArrayDataModel;
7 var List = cr.ui.List;
8 var ListItem = cr.ui.ListItem;
9 var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
10 var Menu = cr.ui.Menu;
11 var MenuItem = cr.ui.MenuItem;
12 var ControlledSettingIndicator = options.ControlledSettingIndicator;
14 /**
15 * Network settings constants. These enums usually match their C++
16 * counterparts.
18 function Constants() {}
20 // Cellular activation states:
21 Constants.ACTIVATION_STATE_UNKNOWN = 0;
22 Constants.ACTIVATION_STATE_ACTIVATED = 1;
23 Constants.ACTIVATION_STATE_ACTIVATING = 2;
24 Constants.ACTIVATION_STATE_NOT_ACTIVATED = 3;
25 Constants.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4;
27 /**
28 * Order in which controls are to appear in the network list sorted by key.
30 Constants.NETWORK_ORDER = ['Ethernet',
31 'WiFi',
32 'Wimax',
33 'Cellular',
34 'VPN',
35 'addConnection'];
37 /**
38 * ID of the menu that is currently visible.
39 * @type {?string}
40 * @private
42 var activeMenu_ = null;
44 /**
45 * Indicates if cellular networks are available.
46 * @type {boolean}
47 * @private
49 var cellularAvailable_ = false;
51 /**
52 * Indicates if cellular networks are enabled.
53 * @type {boolean}
54 * @private
56 var cellularEnabled_ = false;
58 /**
59 * Indicates if cellular device supports network scanning.
60 * @type {boolean}
61 * @private
63 var cellularSupportsScan_ = false;
65 /**
66 * Indicates the current SIM lock type of the cellular device.
67 * @type {boolean}
68 * @private
70 var cellularSimLockType_ = '';
72 /**
73 * Indicates whether the SIM card is absent on the cellular device.
74 * @type {boolean}
75 * @private
77 var cellularSimAbsent_ = false;
79 /**
80 * Indicates if WiMAX networks are available.
81 * @type {boolean}
82 * @private
84 var wimaxAvailable_ = false;
86 /**
87 * Indicates if WiMAX networks are enabled.
88 * @type {boolean}
89 * @private
91 var wimaxEnabled_ = false;
93 /**
94 * Indicates if mobile data roaming is enabled.
95 * @type {boolean}
96 * @private
98 var enableDataRoaming_ = false;
101 * Icon to use when not connected to a particular type of network.
102 * @type {!Object.<string, string>} Mapping of network type to icon data url.
103 * @private
105 var defaultIcons_ = {};
108 * Returns the display name for 'network'.
109 * @param {Object} data The network data dictionary.
111 function getNetworkName(data) {
112 if (data.Type == 'Ethernet')
113 return loadTimeData.getString('ethernetName');
114 return data.Name;
118 * Create an element in the network list for controlling network
119 * connectivity.
120 * @param {Object} data Description of the network list or command.
121 * @constructor
122 * @extends {cr.ui.ListItem}
124 function NetworkListItem(data) {
125 var el = cr.doc.createElement('li');
126 el.data_ = {};
127 for (var key in data)
128 el.data_[key] = data[key];
129 NetworkListItem.decorate(el);
130 return el;
134 * @param {string} action An action to send to coreOptionsUserMetricsAction.
136 function sendChromeMetricsAction(action) {
137 chrome.send('coreOptionsUserMetricsAction', [action]);
141 * Decorate an element as a NetworkListItem.
142 * @param {!Element} el The element to decorate.
144 NetworkListItem.decorate = function(el) {
145 el.__proto__ = NetworkListItem.prototype;
146 el.decorate();
149 NetworkListItem.prototype = {
150 __proto__: ListItem.prototype,
153 * Description of the network group or control.
154 * @type {Object.<string,Object>}
155 * @private
157 data_: null,
160 * Element for the control's subtitle.
161 * @type {?Element}
162 * @private
164 subtitle_: null,
167 * Icon for the network control.
168 * @type {?Element}
169 * @private
171 icon_: null,
174 * Indicates if in the process of connecting to a network.
175 * @type {boolean}
176 * @private
178 connecting_: false,
181 * Description of the network control.
182 * @type {Object}
184 get data() {
185 return this.data_;
189 * Text label for the subtitle.
190 * @type {string}
192 set subtitle(text) {
193 if (text)
194 this.subtitle_.textContent = text;
195 this.subtitle_.hidden = !text;
199 * URL for the network icon.
200 * @type {string}
202 set iconURL(iconURL) {
203 this.icon_.style.backgroundImage = url(iconURL);
207 * Type of network icon. Each type corresponds to a CSS rule.
208 * @type {string}
210 set iconType(type) {
211 if (defaultIcons_[type])
212 this.iconURL = defaultIcons_[type];
213 else
214 this.icon_.classList.add('network-' + type.toLowerCase());
218 * Indicates if the network is in the process of being connected.
219 * @type {boolean}
221 set connecting(state) {
222 this.connecting_ = state;
223 if (state)
224 this.icon_.classList.add('network-connecting');
225 else
226 this.icon_.classList.remove('network-connecting');
230 * Indicates if the network is in the process of being connected.
231 * @type {boolean}
233 get connecting() {
234 return this.connecting_;
238 * Set the direction of the text.
239 * @param {string} direction The direction of the text, e.g. 'ltr'.
241 setSubtitleDirection: function(direction) {
242 this.subtitle_.dir = direction;
246 * Indicate that the selector arrow should be shown.
248 showSelector: function() {
249 this.subtitle_.classList.add('network-selector');
253 * Adds an indicator to show that the network is policy managed.
255 showManagedNetworkIndicator: function() {
256 this.appendChild(new ManagedNetworkIndicator());
259 /** @override */
260 decorate: function() {
261 ListItem.prototype.decorate.call(this);
262 this.className = 'network-group';
263 this.icon_ = this.ownerDocument.createElement('div');
264 this.icon_.className = 'network-icon';
265 this.appendChild(this.icon_);
266 var textContent = this.ownerDocument.createElement('div');
267 textContent.className = 'network-group-labels';
268 this.appendChild(textContent);
269 var categoryLabel = this.ownerDocument.createElement('div');
270 var title;
271 if (this.data_.key == 'addConnection')
272 title = 'addConnectionTitle';
273 else
274 title = this.data_.key.toLowerCase() + 'Title';
275 categoryLabel.className = 'network-title';
276 categoryLabel.textContent = loadTimeData.getString(title);
277 textContent.appendChild(categoryLabel);
278 this.subtitle_ = this.ownerDocument.createElement('div');
279 this.subtitle_.className = 'network-subtitle';
280 textContent.appendChild(this.subtitle_);
285 * Creates a control that displays a popup menu when clicked.
286 * @param {Object} data Description of the control.
287 * @constructor
288 * @extends {NetworkListItem}
290 function NetworkMenuItem(data) {
291 var el = new NetworkListItem(data);
292 el.__proto__ = NetworkMenuItem.prototype;
293 el.decorate();
294 return el;
297 NetworkMenuItem.prototype = {
298 __proto__: NetworkListItem.prototype,
301 * Popup menu element.
302 * @type {?Element}
303 * @private
305 menu_: null,
307 /** @override */
308 decorate: function() {
309 this.subtitle = null;
310 if (this.data.iconType)
311 this.iconType = this.data.iconType;
312 this.addEventListener('click', function() {
313 this.showMenu();
318 * Retrieves the ID for the menu.
320 getMenuName: function() {
321 return this.data_.key.toLowerCase() + '-network-menu';
325 * Creates a popup menu for the control.
326 * @return {Element} The newly created menu.
328 createMenu: function() {
329 if (this.data.menu) {
330 var menu = this.ownerDocument.createElement('div');
331 menu.id = this.getMenuName();
332 menu.className = 'network-menu';
333 menu.hidden = true;
334 Menu.decorate(menu);
335 for (var i = 0; i < this.data.menu.length; i++) {
336 var entry = this.data.menu[i];
337 createCallback_(menu, null, entry.label, entry.command);
339 return menu;
341 return null;
344 canUpdateMenu: function() {
345 return false;
349 * Displays a popup menu.
351 showMenu: function() {
352 var rebuild = false;
353 // Force a rescan if opening the menu for WiFi networks to ensure the
354 // list is up to date. Networks are periodically rescanned, but depending
355 // on timing, there could be an excessive delay before the first rescan
356 // unless forced.
357 var rescan = !activeMenu_ && this.data_.key == 'WiFi';
358 if (!this.menu_) {
359 rebuild = true;
360 var existing = $(this.getMenuName());
361 if (existing) {
362 if (this.updateMenu())
363 return;
364 closeMenu_();
366 this.menu_ = this.createMenu();
367 this.menu_.addEventListener('mousedown', function(e) {
368 // Prevent blurring of list, which would close the menu.
369 e.preventDefault();
371 var parent = $('network-menus');
372 if (existing)
373 parent.replaceChild(this.menu_, existing);
374 else
375 parent.appendChild(this.menu_);
377 var top = this.offsetTop + this.clientHeight;
378 var menuId = this.getMenuName();
379 if (menuId != activeMenu_ || rebuild) {
380 closeMenu_();
381 activeMenu_ = menuId;
382 this.menu_.style.setProperty('top', top + 'px');
383 this.menu_.hidden = false;
385 if (rescan) {
386 // TODO(stevenjb): chrome.networkingPrivate.requestNetworkScan
387 chrome.send('requestNetworkScan');
393 * Creates a control for selecting or configuring a network connection based
394 * on the type of connection (e.g. wifi versus vpn).
395 * @param {{key: string, networkList: Array.<Object>}} data Description of the
396 * network.
397 * @constructor
398 * @extends {NetworkMenuItem}
400 function NetworkSelectorItem(data) {
401 var el = new NetworkMenuItem(data);
402 el.__proto__ = NetworkSelectorItem.prototype;
403 el.decorate();
404 return el;
407 NetworkSelectorItem.prototype = {
408 __proto__: NetworkMenuItem.prototype,
410 /** @override */
411 decorate: function() {
412 // TODO(kevers): Generalize method of setting default label.
413 var policyManaged = false;
414 this.subtitle = loadTimeData.getString('OncConnectionStateNotConnected');
415 var list = this.data_.networkList;
416 var candidateURL = null;
417 for (var i = 0; i < list.length; i++) {
418 var networkDetails = list[i];
419 if (networkDetails.ConnectionState == 'Connecting' ||
420 networkDetails.ConnectionState == 'Connected') {
421 this.subtitle = getNetworkName(networkDetails);
422 this.setSubtitleDirection('ltr');
423 policyManaged = networkDetails.policyManaged;
424 candidateURL = networkDetails.iconURL;
425 // Only break when we see a connecting network as it is possible to
426 // have a connected network and a connecting network at the same
427 // time.
428 if (networkDetails.ConnectionState == 'Connecting') {
429 this.connecting = true;
430 candidateURL = null;
431 break;
435 if (candidateURL)
436 this.iconURL = candidateURL;
437 else
438 this.iconType = this.data.key;
440 this.showSelector();
442 if (policyManaged)
443 this.showManagedNetworkIndicator();
445 if (activeMenu_ == this.getMenuName()) {
446 // Menu is already showing and needs to be updated. Explicitly calling
447 // show menu will force the existing menu to be replaced. The call
448 // is deferred in order to ensure that position of this element has
449 // beem properly updated.
450 var self = this;
451 setTimeout(function() {self.showMenu();}, 0);
456 * Creates a menu for selecting, configuring or disconnecting from a
457 * network.
458 * @return {!Element} The newly created menu.
460 createMenu: function() {
461 var menu = this.ownerDocument.createElement('div');
462 menu.id = this.getMenuName();
463 menu.className = 'network-menu';
464 menu.hidden = true;
465 Menu.decorate(menu);
466 var addendum = [];
467 if (this.data_.key == 'WiFi') {
468 addendum.push({
469 label: loadTimeData.getString('joinOtherNetwork'),
470 command: createAddConnectionCallback_('WiFi'),
471 data: {}
473 } else if (this.data_.key == 'Cellular') {
474 if (cellularEnabled_ && cellularSupportsScan_) {
475 addendum.push({
476 label: loadTimeData.getString('otherCellularNetworks'),
477 command: createAddConnectionCallback_('Cellular'),
478 addClass: ['other-cellulars'],
479 data: {}
483 var label = enableDataRoaming_ ? 'disableDataRoaming' :
484 'enableDataRoaming';
485 var disabled = !loadTimeData.getValue('loggedInAsOwner');
486 var entry = {label: loadTimeData.getString(label),
487 data: {}};
488 if (disabled) {
489 entry.command = null;
490 entry.tooltip =
491 loadTimeData.getString('dataRoamingDisableToggleTooltip');
492 } else {
493 var self = this;
494 entry.command = function() {
495 options.Preferences.setBooleanPref(
496 'cros.signed.data_roaming_enabled',
497 !enableDataRoaming_, true);
498 // Force revalidation of the menu the next time it is displayed.
499 self.menu_ = null;
502 addendum.push(entry);
504 var list = this.data.rememberedNetworks;
505 if (list && list.length > 0) {
506 var callback = function(list) {
507 $('remembered-network-list').clear();
508 var dialog = options.PreferredNetworks.getInstance();
509 PageManager.showPageByName('preferredNetworksPage', false);
510 dialog.update(list);
511 sendChromeMetricsAction('Options_NetworkShowPreferred');
513 addendum.push({label: loadTimeData.getString('preferredNetworks'),
514 command: callback,
515 data: list});
518 var networkGroup = this.ownerDocument.createElement('div');
519 networkGroup.className = 'network-menu-group';
520 list = this.data.networkList;
521 var empty = !list || list.length == 0;
522 if (list) {
523 for (var i = 0; i < list.length; i++) {
524 var data = list[i];
525 this.createNetworkOptionsCallback_(networkGroup, data);
526 if (data.ConnectionState == 'Connected') {
527 if (data.Type == 'VPN') {
528 var disconnectCallback = function() {
529 sendChromeMetricsAction('Options_NetworkDisconnectVPN');
530 // TODO(stevenjb): chrome.networkingPrivate.startDisconnect
531 chrome.send('startDisconnect', [data.servicePath]);
533 // Add separator
534 addendum.push({});
535 addendum.push({label: loadTimeData.getString('disconnectNetwork'),
536 command: disconnectCallback,
537 data: data});
542 if (this.data_.key == 'WiFi' || this.data_.key == 'Wimax' ||
543 this.data_.key == 'Cellular') {
544 addendum.push({});
545 if (this.data_.key == 'WiFi') {
546 addendum.push({
547 label: loadTimeData.getString('turnOffWifi'),
548 command: function() {
549 sendChromeMetricsAction('Options_NetworkWifiToggle');
550 // TODO(stevenjb): chrome.networkingPrivate.disableNetworkType
551 chrome.send('disableNetworkType', ['WiFi']);
553 data: {}});
554 } else if (this.data_.key == 'Wimax') {
555 addendum.push({
556 label: loadTimeData.getString('turnOffWimax'),
557 command: function() {
558 // TODO(stevenjb): chrome.networkingPrivate.disableNetworkType
559 chrome.send('disableNetworkType', ['Wimax']);
561 data: {}});
562 } else if (this.data_.key == 'Cellular') {
563 addendum.push({
564 label: loadTimeData.getString('turnOffCellular'),
565 command: function() {
566 // TODO(stevenjb): chrome.networkingPrivate.disableNetworkType
567 chrome.send('disableNetworkType', ['Cellular']);
569 data: {}});
572 if (!empty)
573 menu.appendChild(networkGroup);
574 if (addendum.length > 0) {
575 var separator = false;
576 if (!empty) {
577 menu.appendChild(MenuItem.createSeparator());
578 separator = true;
580 for (var i = 0; i < addendum.length; i++) {
581 var value = addendum[i];
582 if (value.data) {
583 var item = createCallback_(menu, value.data, value.label,
584 value.command);
585 if (value.tooltip)
586 item.title = value.tooltip;
587 if (value.addClass)
588 item.classList.add(value.addClass);
589 separator = false;
590 } else if (!separator) {
591 menu.appendChild(MenuItem.createSeparator());
592 separator = true;
596 return menu;
600 * Determines if a menu can be updated on the fly. Menus that cannot be
601 * updated are fully regenerated using createMenu. The advantage of
602 * updating a menu is that it can preserve ordering of networks avoiding
603 * entries from jumping around after an update.
605 canUpdateMenu: function() {
606 return this.data_.key == 'WiFi' && activeMenu_ == this.getMenuName();
610 * Updates an existing menu. Updated menus preserve ordering of prior
611 * entries. During the update process, the ordering may differ from the
612 * preferred ordering as determined by the network library. If the
613 * ordering becomes potentially out of sync, then the updated menu is
614 * marked for disposal on close. Reopening the menu will force a
615 * regeneration, which will in turn fix the ordering.
616 * @return {boolean} True if successfully updated.
618 updateMenu: function() {
619 if (!this.canUpdateMenu())
620 return false;
621 var oldMenu = $(this.getMenuName());
622 var group = oldMenu.getElementsByClassName('network-menu-group')[0];
623 if (!group)
624 return false;
625 var newMenu = this.createMenu();
626 var discardOnClose = false;
627 var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu);
628 var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu);
629 for (var key in oldNetworkButtons) {
630 if (newNetworkButtons[key]) {
631 group.replaceChild(newNetworkButtons[key].button,
632 oldNetworkButtons[key].button);
633 if (newNetworkButtons[key].index != oldNetworkButtons[key].index)
634 discardOnClose = true;
635 newNetworkButtons[key] = null;
636 } else {
637 // Leave item in list to prevent network items from jumping due to
638 // deletions.
639 oldNetworkButtons[key].disabled = true;
640 discardOnClose = true;
643 for (var key in newNetworkButtons) {
644 var entry = newNetworkButtons[key];
645 if (entry) {
646 group.appendChild(entry.button);
647 discardOnClose = true;
650 oldMenu.data = {discardOnClose: discardOnClose};
651 return true;
655 * Extracts a mapping of network names to menu element and position.
656 * @param {!Element} menu The menu to process.
657 * @return {Object.<string, {index: number, button: Element}>}
658 * Network mapping.
659 * @private
661 extractNetworkConnectButtons_: function(menu) {
662 var group = menu.getElementsByClassName('network-menu-group')[0];
663 var networkButtons = {};
664 if (!group)
665 return networkButtons;
666 var buttons = group.getElementsByClassName('network-menu-item');
667 for (var i = 0; i < buttons.length; i++) {
668 var label = buttons[i].data.label;
669 networkButtons[label] = {index: i, button: buttons[i]};
671 return networkButtons;
675 * Adds a menu item for showing network details.
676 * @param {!Element} parent The parent element.
677 * @param {Object} data Description of the network.
678 * @private
680 createNetworkOptionsCallback_: function(parent, data) {
681 var menuItem = createCallback_(parent,
682 data,
683 getNetworkName(data),
684 'showDetails',
685 data.iconURL);
686 if (data.policyManaged)
687 menuItem.appendChild(new ManagedNetworkIndicator());
688 if (data.ConnectionState == 'Connected' ||
689 data.ConnectionState == 'Connecting') {
690 var label = menuItem.getElementsByClassName(
691 'network-menu-item-label')[0];
692 label.classList.add('active-network');
698 * Creates a button-like control for configurating internet connectivity.
699 * @param {{key: string, subtitle: string, command: Function}} data
700 * Description of the network control.
701 * @constructor
702 * @extends {NetworkListItem}
704 function NetworkButtonItem(data) {
705 var el = new NetworkListItem(data);
706 el.__proto__ = NetworkButtonItem.prototype;
707 el.decorate();
708 return el;
711 NetworkButtonItem.prototype = {
712 __proto__: NetworkListItem.prototype,
714 /** @override */
715 decorate: function() {
716 if (this.data.subtitle)
717 this.subtitle = this.data.subtitle;
718 else
719 this.subtitle = null;
720 if (this.data.command)
721 this.addEventListener('click', this.data.command);
722 if (this.data.iconURL)
723 this.iconURL = this.data.iconURL;
724 else if (this.data.iconType)
725 this.iconType = this.data.iconType;
726 if (this.data.policyManaged)
727 this.showManagedNetworkIndicator();
732 * Adds a command to a menu for modifying network settings.
733 * @param {!Element} menu Parent menu.
734 * @param {Object} data Description of the network.
735 * @param {!string} label Display name for the menu item.
736 * @param {?(string|!Function)} command Callback function or name
737 * of the command for |networkCommand|.
738 * @param {string=} opt_iconURL Optional URL to an icon for the menu item.
739 * @return {!Element} The created menu item.
740 * @private
742 function createCallback_(menu, data, label, command, opt_iconURL) {
743 var button = menu.ownerDocument.createElement('div');
744 button.className = 'network-menu-item';
746 var buttonIcon = menu.ownerDocument.createElement('div');
747 buttonIcon.className = 'network-menu-item-icon';
748 button.appendChild(buttonIcon);
749 if (opt_iconURL)
750 buttonIcon.style.backgroundImage = url(opt_iconURL);
752 var buttonLabel = menu.ownerDocument.createElement('span');
753 buttonLabel.className = 'network-menu-item-label';
754 buttonLabel.textContent = label;
755 button.appendChild(buttonLabel);
756 var callback = null;
757 if (typeof command == 'string') {
758 var type = data.Type;
759 var path = data.servicePath;
760 callback = function() {
761 chrome.send('networkCommand', [type, path, command]);
762 closeMenu_();
764 } else if (command != null) {
765 if (data) {
766 callback = function() {
767 (/** @type {Function} */(command))(data);
768 closeMenu_();
770 } else {
771 callback = function() {
772 (/** @type {Function} */(command))();
773 closeMenu_();
777 if (callback != null)
778 button.addEventListener('click', callback);
779 else
780 buttonLabel.classList.add('network-disabled-control');
782 button.data = {label: label};
783 MenuItem.decorate(button);
784 menu.appendChild(button);
785 return button;
789 * A list of controls for manipulating network connectivity.
790 * @constructor
791 * @extends {cr.ui.List}
793 var NetworkList = cr.ui.define('list');
795 NetworkList.prototype = {
796 __proto__: List.prototype,
798 /** @override */
799 decorate: function() {
800 List.prototype.decorate.call(this);
801 this.startBatchUpdates();
802 this.autoExpands = true;
803 this.dataModel = new ArrayDataModel([]);
804 this.selectionModel = new ListSingleSelectionModel();
805 this.addEventListener('blur', this.onBlur_.bind(this));
806 this.selectionModel.addEventListener('change',
807 this.onSelectionChange_.bind(this));
809 // Wi-Fi control is always visible.
810 this.update({key: 'WiFi', networkList: []});
812 var entryAddWifi = {
813 label: loadTimeData.getString('addConnectionWifi'),
814 command: createAddConnectionCallback_('WiFi')
816 var entryAddVPN = {
817 label: loadTimeData.getString('addConnectionVPN'),
818 command: createAddConnectionCallback_('VPN')
820 this.update({key: 'addConnection',
821 iconType: 'add-connection',
822 menu: [entryAddWifi, entryAddVPN]
825 var prefs = options.Preferences.getInstance();
826 prefs.addEventListener('cros.signed.data_roaming_enabled',
827 function(event) {
828 enableDataRoaming_ = event.value.value;
830 this.endBatchUpdates();
834 * When the list loses focus, unselect all items in the list and close the
835 * active menu.
836 * @private
838 onBlur_: function() {
839 this.selectionModel.unselectAll();
840 closeMenu_();
844 * Close bubble and menu when a different list item is selected.
845 * @param {Event} event Event detailing the selection change.
846 * @private
848 onSelectionChange_: function(event) {
849 PageManager.hideBubble();
850 // A list item may temporarily become unselected while it is constructing
851 // its menu. The menu should therefore only be closed if a different item
852 // is selected, not when the menu's owner item is deselected.
853 if (activeMenu_) {
854 for (var i = 0; i < event.changes.length; ++i) {
855 if (event.changes[i].selected) {
856 var item = this.dataModel.item(event.changes[i].index);
857 if (!item.getMenuName || item.getMenuName() != activeMenu_) {
858 closeMenu_();
859 return;
867 * Finds the index of a network item within the data model based on
868 * category.
869 * @param {string} key Unique key for the item in the list.
870 * @return {(number|undefined)} The index of the network item, or
871 * |undefined| if it is not found.
873 indexOf: function(key) {
874 var size = this.dataModel.length;
875 for (var i = 0; i < size; i++) {
876 var entry = this.dataModel.item(i);
877 if (entry.key == key)
878 return i;
880 return undefined;
884 * Updates a network control.
885 * @param {Object.<string,string>} data Description of the entry.
887 update: function(data) {
888 this.startBatchUpdates();
889 var index = this.indexOf(data.key);
890 if (index == undefined) {
891 // Find reference position for adding the element. We cannot hide
892 // individual list elements, thus we need to conditionally add or
893 // remove elements and cannot rely on any element having a fixed index.
894 for (var i = 0; i < Constants.NETWORK_ORDER.length; i++) {
895 if (data.key == Constants.NETWORK_ORDER[i]) {
896 data.sortIndex = i;
897 break;
900 var referenceIndex = -1;
901 for (var i = 0; i < this.dataModel.length; i++) {
902 var entry = this.dataModel.item(i);
903 if (entry.sortIndex < data.sortIndex)
904 referenceIndex = i;
905 else
906 break;
908 if (referenceIndex == -1) {
909 // Prepend to the start of the list.
910 this.dataModel.splice(0, 0, data);
911 } else if (referenceIndex == this.dataModel.length) {
912 // Append to the end of the list.
913 this.dataModel.push(data);
914 } else {
915 // Insert after the reference element.
916 this.dataModel.splice(referenceIndex + 1, 0, data);
918 } else {
919 var entry = this.dataModel.item(index);
920 data.sortIndex = entry.sortIndex;
921 this.dataModel.splice(index, 1, data);
923 this.endBatchUpdates();
926 /** @override */
927 createItem: function(entry) {
928 if (entry.networkList)
929 return new NetworkSelectorItem(entry);
930 if (entry.command)
931 return new NetworkButtonItem(entry);
932 if (entry.menu)
933 return new NetworkMenuItem(entry);
934 return undefined;
938 * Deletes an element from the list.
939 * @param {string} key Unique identifier for the element.
941 deleteItem: function(key) {
942 var index = this.indexOf(key);
943 if (index != undefined)
944 this.dataModel.splice(index, 1);
948 * Updates the state of a toggle button.
949 * @param {string} key Unique identifier for the element.
950 * @param {boolean} active Whether the control is active.
952 updateToggleControl: function(key, active) {
953 var index = this.indexOf(key);
954 if (index != undefined) {
955 var entry = this.dataModel.item(index);
956 entry.iconType = active ? 'control-active' :
957 'control-inactive';
958 this.update(entry);
964 * Sets the default icon to use for each network type if disconnected.
965 * @param {!Object.<string, string>} data Mapping of network type to icon
966 * data url.
968 NetworkList.setDefaultNetworkIcons = function(data) {
969 defaultIcons_ = Object.create(data);
973 * Chrome callback for updating network controls.
974 * @param {{wiredList: Array, wirelessList: Array, vpnList: Array,
975 * rememberedList: Array, wifiAvailable: boolean, wifiEnabled: boolean,
976 * wimaxAvailable: boolean, wimaxEnabled: boolean,
977 * cellularAvailable: boolean, cellularEnabled: boolean,
978 * cellularSupportsScan: boolean}} data Description of available network
979 * devices and their corresponding state.
981 NetworkList.refreshNetworkData = function(data) {
982 var networkList = $('network-list');
983 networkList.startBatchUpdates();
984 cellularAvailable_ = data.cellularAvailable;
985 cellularEnabled_ = data.cellularEnabled;
986 cellularSupportsScan_ = data.cellularSupportsScan;
987 cellularSimAbsent_ = data.cellularSimAbsent;
988 cellularSimLockType_ = data.cellularSimLockType;
989 wimaxAvailable_ = data.wimaxAvailable;
990 wimaxEnabled_ = data.wimaxEnabled;
992 // Only show Ethernet control if connected.
993 var ethernetConnection = getConnection_(data.wiredList);
994 if (ethernetConnection) {
995 var type = String('Ethernet');
996 var path = ethernetConnection.servicePath;
997 var ethernetOptions = function() {
998 chrome.send('networkCommand', [type, path, 'showDetails']);
1000 networkList.update(
1001 { key: 'Ethernet',
1002 subtitle: loadTimeData.getString('OncConnectionStateConnected'),
1003 iconURL: ethernetConnection.iconURL,
1004 command: ethernetOptions,
1005 policyManaged: ethernetConnection.policyManaged }
1007 } else {
1008 networkList.deleteItem('Ethernet');
1011 if (data.wifiEnabled)
1012 loadData_('WiFi', data.wirelessList, data.rememberedList);
1013 else
1014 addEnableNetworkButton_('WiFi');
1016 // Only show cellular control if available.
1017 if (data.cellularAvailable) {
1018 if (data.cellularEnabled)
1019 loadData_('Cellular', data.wirelessList, data.rememberedList);
1020 else
1021 addEnableNetworkButton_('Cellular');
1022 } else {
1023 networkList.deleteItem('Cellular');
1026 // Only show wimax control if available. Uses cellular icons.
1027 if (data.wimaxAvailable) {
1028 if (data.wimaxEnabled)
1029 loadData_('Wimax', data.wirelessList, data.rememberedList);
1030 else
1031 addEnableNetworkButton_('Wimax');
1032 } else {
1033 networkList.deleteItem('Wimax');
1036 // Only show VPN control if there is at least one VPN configured.
1037 if (data.vpnList.length > 0)
1038 loadData_('VPN', data.vpnList, data.rememberedList);
1039 else
1040 networkList.deleteItem('VPN');
1041 networkList.endBatchUpdates();
1045 * Replaces a network menu with a button for enabling the network type.
1046 * @param {string} type The type of network (WiFi, Cellular or Wimax).
1047 * @private
1049 function addEnableNetworkButton_(type) {
1050 var subtitle = loadTimeData.getString('networkDisabled');
1051 var icon = (type == 'Wimax') ? 'Cellular' : type;
1052 var enableNetwork = function() {
1053 if (type == 'WiFi')
1054 sendChromeMetricsAction('Options_NetworkWifiToggle');
1055 if (type == 'Cellular') {
1056 if (cellularSimLockType_) {
1057 chrome.send('simOperation', ['unlock']);
1058 return;
1059 } else if (cellularEnabled_ && cellularSimAbsent_) {
1060 chrome.send('simOperation', ['configure']);
1061 return;
1064 // TODO(stevenjb): chrome.networkingPrivate.enableNetworkType
1065 chrome.send('enableNetworkType', [type]);
1067 $('network-list').update({key: type,
1068 subtitle: subtitle,
1069 iconType: icon,
1070 command: enableNetwork});
1074 * Element for indicating a policy managed network.
1075 * @constructor
1076 * @extends {options.ControlledSettingIndicator}
1078 function ManagedNetworkIndicator() {
1079 var el = cr.doc.createElement('span');
1080 el.__proto__ = ManagedNetworkIndicator.prototype;
1081 el.decorate();
1082 return el;
1085 ManagedNetworkIndicator.prototype = {
1086 __proto__: ControlledSettingIndicator.prototype,
1088 /** @override */
1089 decorate: function() {
1090 ControlledSettingIndicator.prototype.decorate.call(this);
1091 this.controlledBy = 'policy';
1092 var policyLabel = loadTimeData.getString('managedNetwork');
1093 this.setAttribute('textPolicy', policyLabel);
1094 this.removeAttribute('tabindex');
1097 /** @override */
1098 handleEvent: function(event) {
1099 // Prevent focus blurring as that would close any currently open menu.
1100 if (event.type == 'mousedown')
1101 return;
1102 ControlledSettingIndicator.prototype.handleEvent.call(this, event);
1106 * Handle mouse events received by the bubble, preventing focus blurring as
1107 * that would close any currently open menu and preventing propagation to
1108 * any elements located behind the bubble.
1109 * @param {Event} event Mouse event.
1111 stopEvent: function(event) {
1112 event.preventDefault();
1113 event.stopPropagation();
1116 /** @override */
1117 toggleBubble: function() {
1118 if (activeMenu_ && !$(activeMenu_).contains(this))
1119 closeMenu_();
1120 ControlledSettingIndicator.prototype.toggleBubble.call(this);
1121 if (this.showingBubble) {
1122 var bubble = PageManager.getVisibleBubble();
1123 bubble.addEventListener('mousedown', this.stopEvent);
1124 bubble.addEventListener('click', this.stopEvent);
1130 * Updates the list of available networks and their status, filtered by
1131 * network type.
1132 * @param {string} type The type of network.
1133 * @param {Array} available The list of available networks and their status.
1134 * @param {Array} remembered The list of remmebered networks.
1136 function loadData_(type, available, remembered) {
1137 var data = {key: type};
1138 var availableNetworks = [];
1139 for (var i = 0; i < available.length; i++) {
1140 if (available[i].Type == type)
1141 availableNetworks.push(available[i]);
1143 data.networkList = availableNetworks;
1144 if (remembered) {
1145 var rememberedNetworks = [];
1146 for (var i = 0; i < remembered.length; i++) {
1147 if (remembered[i].Type == type)
1148 rememberedNetworks.push(remembered[i]);
1150 data.rememberedNetworks = rememberedNetworks;
1152 $('network-list').update(data);
1156 * Hides the currently visible menu.
1157 * @private
1159 function closeMenu_() {
1160 if (activeMenu_) {
1161 var menu = $(activeMenu_);
1162 menu.hidden = true;
1163 if (menu.data && menu.data.discardOnClose)
1164 menu.parentNode.removeChild(menu);
1165 activeMenu_ = null;
1170 * Fetches the active connection.
1171 * @param {Array.<Object>} networkList List of networks.
1172 * @return {Object}
1173 * @private
1175 function getConnection_(networkList) {
1176 if (!networkList)
1177 return null;
1178 for (var i = 0; i < networkList.length; i++) {
1179 var entry = networkList[i];
1180 if (entry.ConnectionState == 'Connected' ||
1181 entry.ConnectionState == 'Connecting')
1182 return entry;
1184 return null;
1188 * Create a callback function that adds a new connection of the given type.
1189 * @param {string} type An ONC network type
1190 * @return {function()} The created callback.
1191 * @private
1193 function createAddConnectionCallback_(type) {
1194 return function() {
1195 if (type == 'WiFi')
1196 sendChromeMetricsAction('Options_NetworkJoinOtherWifi');
1197 else if (type == 'VPN')
1198 sendChromeMetricsAction('Options_NetworkJoinOtherVPN');
1199 chrome.send('networkCommand', [type, '', 'add']);
1204 * Whether the Network list is disabled. Only used for display purpose.
1206 cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR);
1208 // Export
1209 return {
1210 NetworkList: NetworkList