 Chromium Code Reviews
 Chromium Code Reviews Issue 1017443002:
  Allow users to add third-party VPNs from the settings page  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@f_3_460428_update_details_dialog
    
  
    Issue 1017443002:
  Allow users to add third-party VPNs from the settings page  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@f_3_460428_update_details_dialog| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 /** | 5 /** | 
| 6 * This partially describes the network list entries passed to | 6 * This partially describes the network list entries passed to | 
| 7 * refreshNetworkData. The contents of those lists actually match | 7 * refreshNetworkData. The contents of those lists actually match | 
| 8 * CrOnc.NetworkConfigType with the addition of the policyManaged property. | 8 * CrOnc.NetworkConfigType with the addition of the policyManaged property. | 
| 9 * TODO(stevenjb): Use networkingPrivate.getNetworks. | 9 * TODO(stevenjb): Use networkingPrivate.getNetworks. | 
| 10 * @typedef {{ | 10 * @typedef {{ | 
| (...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 370 Menu.decorate(menu); | 370 Menu.decorate(menu); | 
| 371 for (var i = 0; i < this.data.menu.length; i++) { | 371 for (var i = 0; i < this.data.menu.length; i++) { | 
| 372 var entry = this.data.menu[i]; | 372 var entry = this.data.menu[i]; | 
| 373 createCallback_(menu, null, entry.label, entry.command); | 373 createCallback_(menu, null, entry.label, entry.command); | 
| 374 } | 374 } | 
| 375 return menu; | 375 return menu; | 
| 376 } | 376 } | 
| 377 return null; | 377 return null; | 
| 378 }, | 378 }, | 
| 379 | 379 | 
| 380 /** | |
| 381 * Determines if a menu can be updated on the fly. Menus that cannot be | |
| 382 * updated are fully regenerated using createMenu. The advantage of | |
| 383 * updating a menu is that it can preserve ordering of networks avoiding | |
| 384 * entries from jumping around after an update. | |
| 385 * @return {boolean} Whether the menu can be updated on the fly. | |
| 386 */ | |
| 380 canUpdateMenu: function() { | 387 canUpdateMenu: function() { | 
| 381 return false; | 388 return false; | 
| 382 }, | 389 }, | 
| 383 | 390 | 
| 384 /** | 391 /** | 
| 392 * Removes the current menu contents, causing it to be regenerated when the | |
| 393 * menu is shown the next time. If the menu is showing right now, its | |
| 394 * contents is regenerated immediately and the menu remains visible. | |
| 
michaelpg
2015/03/17 19:28:16
nit: s/is/are
 
bartfab (slow)
2015/03/18 21:27:15
Done.
 | |
| 395 */ | |
| 396 refreshMenu: function() { | |
| 397 this.menu_ = null; | |
| 398 if (activeMenu_ == this.getMenuName()) | |
| 399 this.showMenu(); | |
| 400 }, | |
| 401 | |
| 402 /** | |
| 385 * Displays a popup menu. | 403 * Displays a popup menu. | 
| 386 */ | 404 */ | 
| 387 showMenu: function() { | 405 showMenu: function() { | 
| 388 var rebuild = false; | 406 var rebuild = false; | 
| 389 // Force a rescan if opening the menu for WiFi networks to ensure the | 407 // Force a rescan if opening the menu for WiFi networks to ensure the | 
| 390 // list is up to date. Networks are periodically rescanned, but depending | 408 // list is up to date. Networks are periodically rescanned, but depending | 
| 391 // on timing, there could be an excessive delay before the first rescan | 409 // on timing, there could be an excessive delay before the first rescan | 
| 392 // unless forced. | 410 // unless forced. | 
| 393 var rescan = !activeMenu_ && this.data_.key == 'WiFi'; | 411 var rescan = !activeMenu_ && this.data_.key == 'WiFi'; | 
| 394 if (!this.menu_) { | 412 if (!this.menu_) { | 
| 395 rebuild = true; | 413 rebuild = true; | 
| 396 var existing = $(this.getMenuName()); | 414 var existing = $(this.getMenuName()); | 
| 397 if (existing) { | 415 if (existing) { | 
| 398 if (this.updateMenu()) | 416 if (this.canUpdateMenu() && this.updateMenu()) | 
| 399 return; | 417 return; | 
| 400 closeMenu_(); | 418 closeMenu_(); | 
| 401 } | 419 } | 
| 402 this.menu_ = this.createMenu(); | 420 this.menu_ = this.createMenu(); | 
| 403 this.menu_.addEventListener('mousedown', function(e) { | 421 this.menu_.addEventListener('mousedown', function(e) { | 
| 404 // Prevent blurring of list, which would close the menu. | 422 // Prevent blurring of list, which would close the menu. | 
| 405 e.preventDefault(); | 423 e.preventDefault(); | 
| 406 }); | 424 }); | 
| 407 var parent = $('network-menus'); | 425 var parent = $('network-menus'); | 
| 408 if (existing) | 426 if (existing) | 
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 492 createMenu: function() { | 510 createMenu: function() { | 
| 493 var menu = this.ownerDocument.createElement('div'); | 511 var menu = this.ownerDocument.createElement('div'); | 
| 494 menu.id = this.getMenuName(); | 512 menu.id = this.getMenuName(); | 
| 495 menu.className = 'network-menu'; | 513 menu.className = 'network-menu'; | 
| 496 menu.hidden = true; | 514 menu.hidden = true; | 
| 497 Menu.decorate(menu); | 515 Menu.decorate(menu); | 
| 498 var addendum = []; | 516 var addendum = []; | 
| 499 if (this.data_.key == 'WiFi') { | 517 if (this.data_.key == 'WiFi') { | 
| 500 addendum.push({ | 518 addendum.push({ | 
| 501 label: loadTimeData.getString('joinOtherNetwork'), | 519 label: loadTimeData.getString('joinOtherNetwork'), | 
| 502 command: createAddConnectionCallback_('WiFi'), | 520 command: createAddNonVPNConnectionCallback_('WiFi'), | 
| 503 data: {} | 521 data: {} | 
| 504 }); | 522 }); | 
| 505 } else if (this.data_.key == 'Cellular') { | 523 } else if (this.data_.key == 'Cellular') { | 
| 506 if (cellularEnabled_ && cellularSupportsScan_) { | 524 if (cellularEnabled_ && cellularSupportsScan_) { | 
| 507 addendum.push({ | 525 addendum.push({ | 
| 508 label: loadTimeData.getString('otherCellularNetworks'), | 526 label: loadTimeData.getString('otherCellularNetworks'), | 
| 509 command: createAddConnectionCallback_('Cellular'), | 527 command: createAddNonVPNConnectionCallback_('Cellular'), | 
| 510 addClass: ['other-cellulars'], | 528 addClass: ['other-cellulars'], | 
| 511 data: {} | 529 data: {} | 
| 512 }); | 530 }); | 
| 513 } | 531 } | 
| 514 | 532 | 
| 515 var label = enableDataRoaming_ ? 'disableDataRoaming' : | 533 var label = enableDataRoaming_ ? 'disableDataRoaming' : | 
| 516 'enableDataRoaming'; | 534 'enableDataRoaming'; | 
| 517 var disabled = !loadTimeData.getValue('loggedInAsOwner'); | 535 var disabled = !loadTimeData.getValue('loggedInAsOwner'); | 
| 518 var entry = {label: loadTimeData.getString(label), | 536 var entry = {label: loadTimeData.getString(label), | 
| 519 data: {}}; | 537 data: {}}; | 
| 520 if (disabled) { | 538 if (disabled) { | 
| 521 entry.command = null; | 539 entry.command = null; | 
| 522 entry.tooltip = | 540 entry.tooltip = | 
| 523 loadTimeData.getString('dataRoamingDisableToggleTooltip'); | 541 loadTimeData.getString('dataRoamingDisableToggleTooltip'); | 
| 524 } else { | 542 } else { | 
| 525 var self = this; | 543 var self = this; | 
| 526 entry.command = function() { | 544 entry.command = function() { | 
| 527 options.Preferences.setBooleanPref( | 545 options.Preferences.setBooleanPref( | 
| 528 'cros.signed.data_roaming_enabled', | 546 'cros.signed.data_roaming_enabled', | 
| 529 !enableDataRoaming_, true); | 547 !enableDataRoaming_, true); | 
| 530 // Force revalidation of the menu the next time it is displayed. | 548 // Force revalidation of the menu the next time it is displayed. | 
| 531 self.menu_ = null; | 549 self.menu_ = null; | 
| 532 }; | 550 }; | 
| 533 } | 551 } | 
| 534 addendum.push(entry); | 552 addendum.push(entry); | 
| 535 } else if (this.data_.key == 'VPN') { | 553 } else if (this.data_.key == 'VPN') { | 
| 536 addendum.push({ | 554 addendum = addendum.concat(createAddVPNConnectionEntries_()); | 
| 537 label: loadTimeData.getString('joinOtherNetwork'), | |
| 538 command: createAddConnectionCallback_('VPN'), | |
| 539 data: {} | |
| 540 }); | |
| 541 } | 555 } | 
| 542 | 556 | 
| 543 var list = this.data.rememberedNetworks; | 557 var list = this.data.rememberedNetworks; | 
| 544 if (list && list.length > 0) { | 558 if (list && list.length > 0) { | 
| 545 var callback = function(list) { | 559 var callback = function(list) { | 
| 546 $('remembered-network-list').clear(); | 560 $('remembered-network-list').clear(); | 
| 547 var dialog = options.PreferredNetworks.getInstance(); | 561 var dialog = options.PreferredNetworks.getInstance(); | 
| 548 PageManager.showPageByName('preferredNetworksPage', false); | 562 PageManager.showPageByName('preferredNetworksPage', false); | 
| 549 dialog.update(list); | 563 dialog.update(list); | 
| 550 sendChromeMetricsAction('Options_NetworkShowPreferred'); | 564 sendChromeMetricsAction('Options_NetworkShowPreferred'); | 
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 629 separator = false; | 643 separator = false; | 
| 630 } else if (!separator) { | 644 } else if (!separator) { | 
| 631 menu.appendChild(MenuItem.createSeparator()); | 645 menu.appendChild(MenuItem.createSeparator()); | 
| 632 separator = true; | 646 separator = true; | 
| 633 } | 647 } | 
| 634 } | 648 } | 
| 635 } | 649 } | 
| 636 return menu; | 650 return menu; | 
| 637 }, | 651 }, | 
| 638 | 652 | 
| 639 /** | 653 /** @override */ | 
| 640 * Determines if a menu can be updated on the fly. Menus that cannot be | |
| 641 * updated are fully regenerated using createMenu. The advantage of | |
| 642 * updating a menu is that it can preserve ordering of networks avoiding | |
| 643 * entries from jumping around after an update. | |
| 644 */ | |
| 645 canUpdateMenu: function() { | 654 canUpdateMenu: function() { | 
| 646 return this.data_.key == 'WiFi' && activeMenu_ == this.getMenuName(); | 655 return this.data_.key == 'WiFi' && activeMenu_ == this.getMenuName(); | 
| 647 }, | 656 }, | 
| 648 | 657 | 
| 649 /** | 658 /** | 
| 650 * Updates an existing menu. Updated menus preserve ordering of prior | 659 * Updates an existing menu. Updated menus preserve ordering of prior | 
| 651 * entries. During the update process, the ordering may differ from the | 660 * entries. During the update process, the ordering may differ from the | 
| 652 * preferred ordering as determined by the network library. If the | 661 * preferred ordering as determined by the network library. If the | 
| 653 * ordering becomes potentially out of sync, then the updated menu is | 662 * ordering becomes potentially out of sync, then the updated menu is | 
| 654 * marked for disposal on close. Reopening the menu will force a | 663 * marked for disposal on close. Reopening the menu will force a | 
| 655 * regeneration, which will in turn fix the ordering. | 664 * regeneration, which will in turn fix the ordering. This method must only | 
| 665 * be called if canUpdateMenu() returned |true|. | |
| 656 * @return {boolean} True if successfully updated. | 666 * @return {boolean} True if successfully updated. | 
| 657 */ | 667 */ | 
| 658 updateMenu: function() { | 668 updateMenu: function() { | 
| 659 if (!this.canUpdateMenu()) | |
| 
michaelpg
2015/03/17 19:28:16
why does this need to be moved out to line 416?
 
bartfab (slow)
2015/03/18 21:27:15
showMenu() needs to be compatible with the base cl
 | |
| 660 return false; | |
| 661 var oldMenu = $(this.getMenuName()); | 669 var oldMenu = $(this.getMenuName()); | 
| 662 var group = oldMenu.getElementsByClassName('network-menu-group')[0]; | 670 var group = oldMenu.getElementsByClassName('network-menu-group')[0]; | 
| 663 if (!group) | 671 if (!group) | 
| 664 return false; | 672 return false; | 
| 665 var newMenu = this.createMenu(); | 673 var newMenu = this.createMenu(); | 
| 666 var discardOnClose = false; | 674 var discardOnClose = false; | 
| 667 var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu); | 675 var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu); | 
| 668 var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu); | 676 var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu); | 
| 669 for (var key in oldNetworkButtons) { | 677 for (var key in oldNetworkButtons) { | 
| 670 if (newNetworkButtons[key]) { | 678 if (newNetworkButtons[key]) { | 
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 817 button.data = {label: label}; | 825 button.data = {label: label}; | 
| 818 MenuItem.decorate(button); | 826 MenuItem.decorate(button); | 
| 819 menu.appendChild(button); | 827 menu.appendChild(button); | 
| 820 return button; | 828 return button; | 
| 821 } | 829 } | 
| 822 | 830 | 
| 823 /** | 831 /** | 
| 824 * A list of controls for manipulating network connectivity. | 832 * A list of controls for manipulating network connectivity. | 
| 825 * @constructor | 833 * @constructor | 
| 826 * @extends {cr.ui.List} | 834 * @extends {cr.ui.List} | 
| 835 * @implements {options.VPNProviders.Observer} | |
| 827 */ | 836 */ | 
| 828 var NetworkList = cr.ui.define('list'); | 837 var NetworkList = cr.ui.define('list'); | 
| 829 | 838 | 
| 830 NetworkList.prototype = { | 839 NetworkList.prototype = { | 
| 831 __proto__: List.prototype, | 840 __proto__: List.prototype, | 
| 832 | 841 | 
| 833 /** @override */ | 842 /** @override */ | 
| 834 decorate: function() { | 843 decorate: function() { | 
| 835 List.prototype.decorate.call(this); | 844 List.prototype.decorate.call(this); | 
| 836 this.startBatchUpdates(); | 845 this.startBatchUpdates(); | 
| 837 this.autoExpands = true; | 846 this.autoExpands = true; | 
| 838 this.dataModel = new ArrayDataModel([]); | 847 this.dataModel = new ArrayDataModel([]); | 
| 839 this.selectionModel = new ListSingleSelectionModel(); | 848 this.selectionModel = new ListSingleSelectionModel(); | 
| 840 this.addEventListener('blur', this.onBlur_.bind(this)); | 849 this.addEventListener('blur', this.onBlur_.bind(this)); | 
| 841 this.selectionModel.addEventListener('change', | 850 this.selectionModel.addEventListener('change', | 
| 842 this.onSelectionChange_.bind(this)); | 851 this.onSelectionChange_.bind(this)); | 
| 843 | 852 | 
| 844 // Wi-Fi control is always visible. | 853 // Wi-Fi control is always visible. | 
| 845 this.update({key: 'WiFi', networkList: []}); | 854 this.update({key: 'WiFi', networkList: []}); | 
| 846 | 855 | 
| 847 var entryAddWifi = { | 856 this.updateAddConnectionMenuEntries_(); | 
| 848 label: loadTimeData.getString('addConnectionWifi'), | |
| 849 command: createAddConnectionCallback_('WiFi') | |
| 850 }; | |
| 851 var entryAddVPN = { | |
| 852 label: loadTimeData.getString('addConnectionVPN'), | |
| 853 command: createAddConnectionCallback_('VPN') | |
| 854 }; | |
| 855 this.update({key: 'addConnection', | |
| 856 iconType: 'add-connection', | |
| 857 menu: [entryAddWifi, entryAddVPN] | |
| 858 }); | |
| 859 | 857 | 
| 860 var prefs = options.Preferences.getInstance(); | 858 var prefs = options.Preferences.getInstance(); | 
| 861 prefs.addEventListener('cros.signed.data_roaming_enabled', | 859 prefs.addEventListener('cros.signed.data_roaming_enabled', | 
| 862 function(event) { | 860 function(event) { | 
| 863 enableDataRoaming_ = event.value.value; | 861 enableDataRoaming_ = event.value.value; | 
| 864 }); | 862 }); | 
| 865 this.endBatchUpdates(); | 863 this.endBatchUpdates(); | 
| 864 | |
| 865 options.VPNProviders.addObserver(this); | |
| 866 }, | 866 }, | 
| 867 | 867 | 
| 868 /** | 868 /** | 
| 869 * Called when the list of VPN providers changes. Refreshes the contents of | |
| 870 * menus that list VPN providers. | |
| 871 * @override | |
| 872 */ | |
| 873 onVPNProvidersChanged() { | |
| 874 // Refresh the contents of the VPN menu. | |
| 875 var index = this.indexOf('VPN'); | |
| 876 if (index != undefined) | |
| 877 this.getListItemByIndex(index).refreshMenu(); | |
| 878 | |
| 879 // Refresh the contents of the "add connection" menu. | |
| 880 this.updateAddConnectionMenuEntries_(); | |
| 881 index = this.indexOf('addConnection'); | |
| 882 if (index != undefined) | |
| 883 this.getListItemByIndex(index).refreshMenu(); | |
| 884 }, | |
| 885 | |
| 886 /** | |
| 887 * Updates the entries in the "add connection" menu, based on the VPN | |
| 888 * providers currently enabled in the primary user's profile. | |
| 889 * @private | |
| 890 */ | |
| 891 updateAddConnectionMenuEntries_() { | |
| 892 var entries = [{ | |
| 893 label: loadTimeData.getString('addConnectionWifi'), | |
| 894 command: createAddNonVPNConnectionCallback_('WiFi') | |
| 895 }]; | |
| 896 entries = entries.concat(createAddVPNConnectionEntries_()); | |
| 897 this.update({key: 'addConnection', | |
| 898 iconType: 'add-connection', | |
| 899 menu: entries | |
| 900 }); | |
| 901 }, | |
| 902 | |
| 903 /** | |
| 869 * When the list loses focus, unselect all items in the list and close the | 904 * When the list loses focus, unselect all items in the list and close the | 
| 870 * active menu. | 905 * active menu. | 
| 871 * @private | 906 * @private | 
| 872 */ | 907 */ | 
| 873 onBlur_: function() { | 908 onBlur_: function() { | 
| 874 this.selectionModel.unselectAll(); | 909 this.selectionModel.unselectAll(); | 
| 875 closeMenu_(); | 910 closeMenu_(); | 
| 876 }, | 911 }, | 
| 877 | 912 | 
| 878 /** | 913 /** | 
| (...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1213 for (var i = 0; i < networkList.length; i++) { | 1248 for (var i = 0; i < networkList.length; i++) { | 
| 1214 var entry = networkList[i]; | 1249 var entry = networkList[i]; | 
| 1215 if (entry.ConnectionState == 'Connected' || | 1250 if (entry.ConnectionState == 'Connected' || | 
| 1216 entry.ConnectionState == 'Connecting') | 1251 entry.ConnectionState == 'Connecting') | 
| 1217 return entry; | 1252 return entry; | 
| 1218 } | 1253 } | 
| 1219 return null; | 1254 return null; | 
| 1220 } | 1255 } | 
| 1221 | 1256 | 
| 1222 /** | 1257 /** | 
| 1223 * Create a callback function that adds a new connection of the given type. | 1258 * Creates a callback function that adds a new connection of the given type. | 
| 1259 * This method may be used for all network types except VPN. | |
| 1224 * @param {string} type An ONC network type | 1260 * @param {string} type An ONC network type | 
| 1225 * @return {function()} The created callback. | 1261 * @return {function()} The created callback. | 
| 1226 * @private | 1262 * @private | 
| 1227 */ | 1263 */ | 
| 1228 function createAddConnectionCallback_(type) { | 1264 function createAddNonVPNConnectionCallback_(type) { | 
| 1229 return function() { | 1265 return function() { | 
| 1230 if (type == 'WiFi') | 1266 if (type == 'WiFi') | 
| 1231 sendChromeMetricsAction('Options_NetworkJoinOtherWifi'); | 1267 sendChromeMetricsAction('Options_NetworkJoinOtherWifi'); | 
| 1232 else if (type == 'VPN') | 1268 chrome.send('addNonVPNConnection', [type]); | 
| 1233 sendChromeMetricsAction('Options_NetworkJoinOtherVPN'); | |
| 1234 chrome.send('addConnection', [type]); | |
| 1235 }; | 1269 }; | 
| 1236 } | 1270 } | 
| 1237 | 1271 | 
| 1238 /** | 1272 /** | 
| 1273 * Creates a callback function that asks the VPN provider identified by | |
| 1274 * |providerID| to show its "add network" dialog. | |
| 1275 * @param {string} providerID The VPN provider's ID, as retrieved from | |
| 1276 * options.VPNProviders.getProviders(). | |
| 1277 * @return {function()} The created callback. | |
| 1278 * @private | |
| 1279 */ | |
| 1280 function createAddVPNConnectionCallback_(providerID) { | |
| 1281 return function() { | |
| 1282 sendChromeMetricsAction( | |
| 1283 options.VPNProviders.isThirdPartyProvider(providerID) ? | |
| 1284 'Options_NetworkAddVPNThirdParty' : | |
| 1285 'Options_NetworkAddVPNBuiltIn'); | |
| 1286 chrome.send('addVPNConnection', [providerID]); | |
| 1287 }; | |
| 1288 } | |
| 1289 | |
| 1290 /** | |
| 1291 * Generates an "add network" entry for each VPN provider currently enabled in | |
| 1292 * the primary user's profile. | |
| 1293 * @return {!Array<Object<{label: string, | |
| 
michaelpg
2015/03/17 19:28:16
The Object<> notation designates a set. To just de
 
bartfab (slow)
2015/03/18 21:27:15
Done.
 | |
| 1294 * command: function(), | |
| 1295 * data: !Object}>>} The list of entries. | |
| 1296 * @private | |
| 1297 */ | |
| 1298 function createAddVPNConnectionEntries_() { | |
| 1299 var entries = []; | |
| 1300 var providers = options.VPNProviders.getProviders(); | |
| 1301 for (var providerID in providers) { | |
| 1302 entries.push({ | |
| 1303 label: loadTimeData.getStringF('addConnectionVPNTemplate', | |
| 1304 providers[providerID]), | |
| 1305 command: createAddVPNConnectionCallback_(providerID), | |
| 1306 data: {} | |
| 1307 }); | |
| 1308 } | |
| 1309 return entries; | |
| 1310 } | |
| 1311 | |
| 1312 /** | |
| 1239 * Whether the Network list is disabled. Only used for display purpose. | 1313 * Whether the Network list is disabled. Only used for display purpose. | 
| 1240 */ | 1314 */ | 
| 1241 cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR); | 1315 cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR); | 
| 1242 | 1316 | 
| 1243 // Export | 1317 // Export | 
| 1244 return { | 1318 return { | 
| 1245 NetworkList: NetworkList | 1319 NetworkList: NetworkList | 
| 1246 }; | 1320 }; | 
| 1247 }); | 1321 }); | 
| OLD | NEW |