OLD | NEW |
| (Empty) |
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. | |
4 | |
5 cr.define('options.network', function() { | |
6 | |
7 var ArrayDataModel = cr.ui.ArrayDataModel; | |
8 var List = cr.ui.List; | |
9 var ListItem = cr.ui.ListItem; | |
10 var Menu = cr.ui.Menu; | |
11 var MenuItem = cr.ui.MenuItem; | |
12 var ControlledSettingIndicator = options.ControlledSettingIndicator; | |
13 | |
14 /** | |
15 * Network settings constants. These enums usually match their C++ | |
16 * counterparts. | |
17 */ | |
18 function Constants() {} | |
19 | |
20 // Network types: | |
21 Constants.TYPE_UNKNOWN = 0; | |
22 Constants.TYPE_ETHERNET = 1; | |
23 Constants.TYPE_WIFI = 2; | |
24 Constants.TYPE_WIMAX = 3; | |
25 Constants.TYPE_BLUETOOTH = 4; | |
26 Constants.TYPE_CELLULAR = 5; | |
27 Constants.TYPE_VPN = 6; | |
28 | |
29 // Cellular activation states: | |
30 Constants.ACTIVATION_STATE_UNKNOWN = 0; | |
31 Constants.ACTIVATION_STATE_ACTIVATED = 1; | |
32 Constants.ACTIVATION_STATE_ACTIVATING = 2; | |
33 Constants.ACTIVATION_STATE_NOT_ACTIVATED = 3; | |
34 Constants.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4; | |
35 | |
36 /** | |
37 * Order in which controls are to appear in the network list sorted by key. | |
38 */ | |
39 Constants.NETWORK_ORDER = ['ethernet', | |
40 'wifi', | |
41 'wimax', | |
42 'cellular', | |
43 'vpn', | |
44 'airplaneMode', | |
45 'addConnection']; | |
46 | |
47 /** | |
48 * Mapping of network category titles to the network type. | |
49 */ | |
50 var categoryMap = { | |
51 'cellular': Constants.TYPE_CELLULAR, | |
52 'ethernet': Constants.TYPE_ETHERNET, | |
53 'wimax': Constants.TYPE_WIMAX, | |
54 'wifi': Constants.TYPE_WIFI, | |
55 'vpn': Constants.TYPE_VPN | |
56 }; | |
57 | |
58 /** | |
59 * ID of the menu that is currently visible. | |
60 * @type {?string} | |
61 * @private | |
62 */ | |
63 var activeMenu_ = null; | |
64 | |
65 /** | |
66 * Indicates if cellular networks are available. | |
67 * @type {boolean} | |
68 * @private | |
69 */ | |
70 var cellularAvailable_ = false; | |
71 | |
72 /** | |
73 * Indicates if cellular networks are enabled. | |
74 * @type {boolean} | |
75 * @private | |
76 */ | |
77 var cellularEnabled_ = false; | |
78 | |
79 /** | |
80 * Indicates if WiMAX networks are available. | |
81 * @type {boolean} | |
82 * @private | |
83 */ | |
84 var wimaxAvailable_ = false; | |
85 | |
86 /** | |
87 * Indicates if WiMAX networks are enabled. | |
88 * @type {boolean} | |
89 * @private | |
90 */ | |
91 var wimaxEnabled_ = false; | |
92 | |
93 /** | |
94 * Indicates if mobile data roaming is enabled. | |
95 * @type {boolean} | |
96 * @private | |
97 */ | |
98 var enableDataRoaming_ = false; | |
99 | |
100 /** | |
101 * Create an element in the network list for controlling network | |
102 * connectivity. | |
103 * @param {Object} data Description of the network list or command. | |
104 * @constructor | |
105 */ | |
106 function NetworkListItem(data) { | |
107 var el = cr.doc.createElement('li'); | |
108 el.data_ = {}; | |
109 for (var key in data) | |
110 el.data_[key] = data[key]; | |
111 NetworkListItem.decorate(el); | |
112 return el; | |
113 } | |
114 | |
115 /** | |
116 * Decorate an element as a NetworkListItem. | |
117 * @param {!Element} el The element to decorate. | |
118 */ | |
119 NetworkListItem.decorate = function(el) { | |
120 el.__proto__ = NetworkListItem.prototype; | |
121 el.decorate(); | |
122 }; | |
123 | |
124 NetworkListItem.prototype = { | |
125 __proto__: ListItem.prototype, | |
126 | |
127 /** | |
128 * Description of the network group or control. | |
129 * @type {Object.<string,Object>} | |
130 * @private | |
131 */ | |
132 data_: null, | |
133 | |
134 /** | |
135 * Element for the control's subtitle. | |
136 * @type {?Element} | |
137 * @private | |
138 */ | |
139 subtitle_: null, | |
140 | |
141 /** | |
142 * Icon for the network control. | |
143 * @type {?Element} | |
144 * @private | |
145 */ | |
146 icon_: null, | |
147 | |
148 /** | |
149 * Indicates if in the process of connecting to a network. | |
150 * @type {boolean} | |
151 * @private | |
152 */ | |
153 connecting_: false, | |
154 | |
155 /** | |
156 * Description of the network control. | |
157 * @type {Object} | |
158 */ | |
159 get data() { | |
160 return this.data_; | |
161 }, | |
162 | |
163 /** | |
164 * Text label for the subtitle. | |
165 * @type {string} | |
166 */ | |
167 set subtitle(text) { | |
168 if (text) | |
169 this.subtitle_.textContent = text; | |
170 this.subtitle_.hidden = !text; | |
171 }, | |
172 | |
173 /** | |
174 * URL for the network icon. | |
175 * @type {string} | |
176 */ | |
177 set iconURL(iconURL) { | |
178 this.icon_.style.backgroundImage = url(iconURL); | |
179 }, | |
180 | |
181 /** | |
182 * Type of network icon. Each type corresponds to a CSS rule. | |
183 * @type {string} | |
184 */ | |
185 set iconType(type) { | |
186 this.icon_.classList.add('network-' + type); | |
187 }, | |
188 | |
189 /** | |
190 * Indicates if the network is in the process of being connected. | |
191 * @type {boolean} | |
192 */ | |
193 set connecting(state) { | |
194 this.connecting_ = state; | |
195 if (state) | |
196 this.icon_.classList.add('network-connecting'); | |
197 else | |
198 this.icon_.classList.remove('network-connecting'); | |
199 }, | |
200 | |
201 /** | |
202 * Indicates if the network is in the process of being connected. | |
203 * @type {boolean} | |
204 */ | |
205 get connecting() { | |
206 return this.connecting_; | |
207 }, | |
208 | |
209 /** | |
210 * Set the direction of the text. | |
211 * @param {string} direction The direction of the text, e.g. 'ltr'. | |
212 */ | |
213 setSubtitleDirection: function(direction) { | |
214 this.subtitle_.dir = direction; | |
215 }, | |
216 | |
217 /** | |
218 * Indicate that the selector arrow should be shown. | |
219 */ | |
220 showSelector: function() { | |
221 this.subtitle_.classList.add('network-selector'); | |
222 }, | |
223 | |
224 /** | |
225 * Adds an indicator to show that the network is policy managed. | |
226 */ | |
227 showManagedNetworkIndicator: function() { | |
228 this.appendChild(new ManagedNetworkIndicator()); | |
229 }, | |
230 | |
231 /* @inheritDoc */ | |
232 decorate: function() { | |
233 ListItem.prototype.decorate.call(this); | |
234 this.className = 'network-group'; | |
235 this.icon_ = this.ownerDocument.createElement('div'); | |
236 this.icon_.className = 'network-icon'; | |
237 this.appendChild(this.icon_); | |
238 var textContent = this.ownerDocument.createElement('div'); | |
239 textContent.className = 'network-group-labels'; | |
240 this.appendChild(textContent); | |
241 var categoryLabel = this.ownerDocument.createElement('div'); | |
242 var title = this.data_.key + 'Title'; | |
243 categoryLabel.className = 'network-title'; | |
244 categoryLabel.textContent = loadTimeData.getString(title); | |
245 textContent.appendChild(categoryLabel); | |
246 this.subtitle_ = this.ownerDocument.createElement('div'); | |
247 this.subtitle_.className = 'network-subtitle'; | |
248 textContent.appendChild(this.subtitle_); | |
249 }, | |
250 }; | |
251 | |
252 /** | |
253 * Creates a control that displays a popup menu when clicked. | |
254 * @param {Object} data Description of the control. | |
255 */ | |
256 function NetworkMenuItem(data) { | |
257 var el = new NetworkListItem(data); | |
258 el.__proto__ = NetworkMenuItem.prototype; | |
259 el.decorate(); | |
260 return el; | |
261 } | |
262 | |
263 NetworkMenuItem.prototype = { | |
264 __proto__: NetworkListItem.prototype, | |
265 | |
266 /** | |
267 * Popup menu element. | |
268 * @type {?Element} | |
269 * @private | |
270 */ | |
271 menu_: null, | |
272 | |
273 /* @inheritDoc */ | |
274 decorate: function() { | |
275 this.subtitle = null; | |
276 if (this.data.iconType) | |
277 this.iconType = this.data.iconType; | |
278 this.addEventListener('click', function() { | |
279 this.showMenu(); | |
280 }); | |
281 }, | |
282 | |
283 /** | |
284 * Retrieves the ID for the menu. | |
285 * @private | |
286 */ | |
287 getMenuName_: function() { | |
288 return this.data_.key + '-network-menu'; | |
289 }, | |
290 | |
291 /** | |
292 * Creates a popup menu for the control. | |
293 * @return {Element} The newly created menu. | |
294 */ | |
295 createMenu: function() { | |
296 if (this.data.menu) { | |
297 var menu = this.ownerDocument.createElement('div'); | |
298 menu.id = this.getMenuName_(); | |
299 menu.className = 'network-menu'; | |
300 menu.hidden = true; | |
301 Menu.decorate(menu); | |
302 for (var i = 0; i < this.data.menu.length; i++) { | |
303 var entry = this.data.menu[i]; | |
304 var button = this.ownerDocument.createElement('div'); | |
305 button.className = 'network-menu-item'; | |
306 var buttonLabel = this.ownerDocument.createElement('div'); | |
307 buttonLabel.className = 'network-menu-item-label'; | |
308 buttonLabel.textContent = entry.label; | |
309 button.appendChild(buttonLabel); | |
310 button.addEventListener('click', entry.command); | |
311 MenuItem.decorate(button); | |
312 menu.appendChild(button); | |
313 } | |
314 return menu; | |
315 } | |
316 return null; | |
317 }, | |
318 | |
319 canUpdateMenu: function() { | |
320 return false; | |
321 }, | |
322 | |
323 /** | |
324 * Displays a popup menu. | |
325 */ | |
326 showMenu: function() { | |
327 var rebuild = false; | |
328 // Force a rescan if opening the menu for WiFi networks to ensure the | |
329 // list is up to date. Networks are periodically rescanned, but depending | |
330 // on timing, there could be an excessive delay before the first rescan | |
331 // unless forced. | |
332 var rescan = !activeMenu_ && this.data_.key == 'wifi'; | |
333 if (!this.menu_) { | |
334 rebuild = true; | |
335 var existing = $(this.getMenuName_()); | |
336 if (existing) { | |
337 if (this.updateMenu()) | |
338 return; | |
339 closeMenu_(); | |
340 } | |
341 this.menu_ = this.createMenu(); | |
342 this.menu_.addEventListener('mousedown', function(e) { | |
343 // Prevent blurring of list, which would close the menu. | |
344 e.preventDefault(); | |
345 }, true); | |
346 var parent = $('network-menus'); | |
347 if (existing) | |
348 parent.replaceChild(this.menu_, existing); | |
349 else | |
350 parent.appendChild(this.menu_); | |
351 } | |
352 var top = this.offsetTop + this.clientHeight; | |
353 var menuId = this.getMenuName_(); | |
354 if (menuId != activeMenu_ || rebuild) { | |
355 closeMenu_(); | |
356 activeMenu_ = menuId; | |
357 this.menu_.style.setProperty('top', top + 'px'); | |
358 this.menu_.hidden = false; | |
359 } | |
360 if (rescan) | |
361 chrome.send('refreshNetworks'); | |
362 }, | |
363 }; | |
364 | |
365 /** | |
366 * Creates a control for selecting or configuring a network connection based | |
367 * on the type of connection (e.g. wifi versus vpn). | |
368 * @param {{key: string, | |
369 * networkList: Array.<Object>} data Description of the network. | |
370 * @constructor | |
371 */ | |
372 function NetworkSelectorItem(data) { | |
373 var el = new NetworkMenuItem(data); | |
374 el.__proto__ = NetworkSelectorItem.prototype; | |
375 el.decorate(); | |
376 return el; | |
377 } | |
378 | |
379 NetworkSelectorItem.prototype = { | |
380 __proto__: NetworkMenuItem.prototype, | |
381 | |
382 /* @inheritDoc */ | |
383 decorate: function() { | |
384 // TODO(kevers): Generalize method of setting default label. | |
385 var policyManaged = false; | |
386 var defaultMessage = this.data_.key == 'wifi' ? | |
387 'networkOffline' : 'networkNotConnected'; | |
388 this.subtitle = loadTimeData.getString(defaultMessage); | |
389 var list = this.data_.networkList; | |
390 var candidateURL = null; | |
391 for (var i = 0; i < list.length; i++) { | |
392 var networkDetails = list[i]; | |
393 if (networkDetails.connecting || networkDetails.connected) { | |
394 this.subtitle = networkDetails.networkName; | |
395 this.setSubtitleDirection('ltr'); | |
396 policyManaged = networkDetails.policyManaged; | |
397 candidateURL = networkDetails.iconURL; | |
398 // Only break when we see a connecting network as it is possible to | |
399 // have a connected network and a connecting network at the same | |
400 // time. | |
401 if (networkDetails.connecting) { | |
402 this.connecting = true; | |
403 candidateURL = null; | |
404 break; | |
405 } | |
406 } | |
407 } | |
408 if (candidateURL) | |
409 this.iconURL = candidateURL; | |
410 else | |
411 this.iconType = this.data.key; | |
412 | |
413 this.showSelector(); | |
414 | |
415 if (policyManaged) | |
416 this.showManagedNetworkIndicator(); | |
417 | |
418 if (activeMenu_ == this.getMenuName_()) { | |
419 // Menu is already showing and needs to be updated. Explicitly calling | |
420 // show menu will force the existing menu to be replaced. The call | |
421 // is deferred in order to ensure that position of this element has | |
422 // beem properly updated. | |
423 var self = this; | |
424 setTimeout(function() {self.showMenu();}, 0); | |
425 } | |
426 }, | |
427 | |
428 /** | |
429 * Creates a menu for selecting, configuring or disconnecting from a | |
430 * network. | |
431 * @return {Element} The newly created menu. | |
432 */ | |
433 createMenu: function() { | |
434 var menu = this.ownerDocument.createElement('div'); | |
435 menu.id = this.getMenuName_(); | |
436 menu.className = 'network-menu'; | |
437 menu.hidden = true; | |
438 Menu.decorate(menu); | |
439 var addendum = []; | |
440 if (this.data_.key == 'wifi') { | |
441 addendum.push({label: loadTimeData.getString('joinOtherNetwork'), | |
442 command: 'connect', | |
443 data: {networkType: Constants.TYPE_WIFI, | |
444 servicePath: '?'}}); | |
445 } else if (this.data_.key == 'cellular') { | |
446 var label = enableDataRoaming_ ? 'disableDataRoaming' : | |
447 'enableDataRoaming'; | |
448 var disabled = !UIAccountTweaks.currentUserIsOwner(); | |
449 var entry = {label: loadTimeData.getString(label), | |
450 data: {}}; | |
451 if (disabled) { | |
452 entry.command = null; | |
453 entry.tooltip = | |
454 loadTimeData.getString('dataRoamingDisableToggleTooltip'); | |
455 } else { | |
456 entry.command = function() { | |
457 options.Preferences.setBooleanPref( | |
458 'cros.signed.data_roaming_enabled', | |
459 !enableDataRoaming_); | |
460 // Force revalidation of the menu the next time it is displayed. | |
461 this.menu_ = null; | |
462 }; | |
463 } | |
464 addendum.push(entry); | |
465 } | |
466 var list = this.data.rememberedNetworks; | |
467 if (list && list.length > 0) { | |
468 var callback = function(list) { | |
469 $('remembered-network-list').clear(); | |
470 var dialog = options.PreferredNetworks.getInstance(); | |
471 OptionsPage.showPageByName('preferredNetworksPage', false); | |
472 dialog.update(list); | |
473 }; | |
474 addendum.push({label: loadTimeData.getString('preferredNetworks'), | |
475 command: callback, | |
476 data: list}); | |
477 } | |
478 | |
479 var networkGroup = this.ownerDocument.createElement('div'); | |
480 networkGroup.className = 'network-menu-group'; | |
481 list = this.data.networkList; | |
482 var empty = !list || list.length == 0; | |
483 if (list) { | |
484 for (var i = 0; i < list.length; i++) { | |
485 var data = list[i]; | |
486 this.createNetworkOptionsCallback_(networkGroup, data); | |
487 if (data.connected) { | |
488 if (data.networkType == Constants.TYPE_VPN) { | |
489 // Add separator | |
490 addendum.push({}); | |
491 var i18nKey = 'disconnectNetwork'; | |
492 addendum.push({label: loadTimeData.getString(i18nKey), | |
493 command: 'disconnect', | |
494 data: data}); | |
495 } | |
496 } | |
497 } | |
498 } | |
499 if (this.data_.key == 'wifi' || this.data_.key == 'wimax' || | |
500 this.data_.key == 'cellular') { | |
501 addendum.push({}); | |
502 if (this.data_.key == 'wifi') { | |
503 addendum.push({label: loadTimeData.getString('turnOffWifi'), | |
504 command: function() { | |
505 chrome.send('disableWifi'); | |
506 }, | |
507 data: {}}); | |
508 } else if (this.data_.key == 'wimax') { | |
509 // TODO(zelidrag): Add proper strings for wimax. | |
510 addendum.push({label: loadTimeData.getString('turnOffCellular'), | |
511 command: function() { | |
512 chrome.send('disableWimax'); | |
513 }, | |
514 data: {}}); | |
515 } else if (this.data_.key == 'cellular') { | |
516 addendum.push({label: loadTimeData.getString('turnOffCellular'), | |
517 command: function() { | |
518 chrome.send('disableCellular'); | |
519 }, | |
520 data: {}}); | |
521 } | |
522 } | |
523 if (!empty) | |
524 menu.appendChild(networkGroup); | |
525 if (addendum.length > 0) { | |
526 var separator = false; | |
527 if (!empty) { | |
528 menu.appendChild(MenuItem.createSeparator()); | |
529 separator = true; | |
530 } | |
531 for (var i = 0; i < addendum.length; i++) { | |
532 var value = addendum[i]; | |
533 if (value.data) { | |
534 var item = this.createCallback_(menu, value.data, value.label, | |
535 value.command); | |
536 if (value.tooltip) | |
537 item.title = value.tooltip; | |
538 separator = false; | |
539 } else if (!separator) { | |
540 menu.appendChild(MenuItem.createSeparator()); | |
541 separator = true; | |
542 } | |
543 } | |
544 } | |
545 return menu; | |
546 }, | |
547 | |
548 /** | |
549 * Determines if a menu can be updated on the fly. Menus that cannot be | |
550 * updated are fully regenerated using createMenu. The advantage of | |
551 * updating a menu is that it can preserve ordering of networks avoiding | |
552 * entries from jumping around after an update. | |
553 */ | |
554 canUpdateMenu: function() { | |
555 return this.data_.key == 'wifi' && activeMenu_ == this.getMenuName_(); | |
556 }, | |
557 | |
558 /** | |
559 * Updates an existing menu. Updated menus preserve ordering of prior | |
560 * entries. During the update process, the ordering may differ from the | |
561 * preferred ordering as determined by the network library. If the | |
562 * ordering becomes potentially out of sync, then the updated menu is | |
563 * marked for disposal on close. Reopening the menu will force a | |
564 * regeneration, which will in turn fix the ordering. | |
565 * @return {boolean} True if successfully updated. | |
566 */ | |
567 updateMenu: function() { | |
568 if (!this.canUpdateMenu()) | |
569 return false; | |
570 var oldMenu = $(this.getMenuName_()); | |
571 var group = oldMenu.getElementsByClassName('network-menu-group')[0]; | |
572 if (!group) | |
573 return false; | |
574 var newMenu = this.createMenu(); | |
575 var discardOnClose = false; | |
576 var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu); | |
577 var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu); | |
578 for (var key in oldNetworkButtons) { | |
579 if (newNetworkButtons[key]) { | |
580 group.replaceChild(newNetworkButtons[key].button, | |
581 oldNetworkButtons[key].button); | |
582 if (newNetworkButtons[key].index != oldNetworkButtons[key].index) | |
583 discardOnClose = true; | |
584 newNetworkButtons[key] = null; | |
585 } else { | |
586 // Leave item in list to prevent network items from jumping due to | |
587 // deletions. | |
588 oldNetworkButtons[key].disabled = true; | |
589 discardOnClose = true; | |
590 } | |
591 } | |
592 for (var key in newNetworkButtons) { | |
593 var entry = newNetworkButtons[key]; | |
594 if (entry) { | |
595 group.appendChild(entry.button); | |
596 discardOnClose = true; | |
597 } | |
598 } | |
599 oldMenu.data = {discardOnClose: discardOnClose}; | |
600 return true; | |
601 }, | |
602 | |
603 /** | |
604 * Extracts a mapping of network names to menu element and position. | |
605 * @param {!Element} menu The menu to process. | |
606 * @return {Object.<string, Element>} Network mapping. | |
607 * @private | |
608 */ | |
609 extractNetworkConnectButtons_: function(menu) { | |
610 var group = menu.getElementsByClassName('network-menu-group')[0]; | |
611 var networkButtons = {}; | |
612 if (!group) | |
613 return networkButtons; | |
614 var buttons = group.getElementsByClassName('network-menu-item'); | |
615 for (var i = 0; i < buttons.length; i++) { | |
616 var label = buttons[i].data.label; | |
617 networkButtons[label] = {index: i, button: buttons[i]}; | |
618 } | |
619 return networkButtons; | |
620 }, | |
621 | |
622 /** | |
623 * Adds a command to a menu for modifying network settings. | |
624 * @param {!Element} menu Parent menu. | |
625 * @param {Object} data Description of the network. | |
626 * @param {string} label Display name for the menu item. | |
627 * @param {string|function} command Callback function or name | |
628 * of the command for |networkCommand|. | |
629 * @return {!Element} The created menu item. | |
630 * @private | |
631 */ | |
632 createCallback_: function(menu, data, label, command) { | |
633 var button = this.ownerDocument.createElement('div'); | |
634 button.className = 'network-menu-item'; | |
635 var buttonLabel = this.ownerDocument.createElement('span'); | |
636 buttonLabel.className = 'network-menu-item-label'; | |
637 buttonLabel.textContent = label; | |
638 button.appendChild(buttonLabel); | |
639 var callback = null; | |
640 if (typeof command == 'string') { | |
641 var type = String(data.networkType); | |
642 var path = data.servicePath; | |
643 callback = function() { | |
644 chrome.send('networkCommand', | |
645 [type, path, command]); | |
646 closeMenu_(); | |
647 }; | |
648 } else if (command != null) { | |
649 callback = function() { | |
650 command(data); | |
651 closeMenu_(); | |
652 }; | |
653 } | |
654 if (callback != null) | |
655 button.addEventListener('click', callback); | |
656 else | |
657 buttonLabel.classList.add('network-disabled-control'); | |
658 | |
659 button.data = {label: label}; | |
660 MenuItem.decorate(button); | |
661 menu.appendChild(button); | |
662 return button; | |
663 }, | |
664 | |
665 /** | |
666 * Adds a menu item for showing network details. | |
667 * @param {!Element} parent The parent element. | |
668 * @param {Object} data Description of the network. | |
669 * @private | |
670 */ | |
671 createNetworkOptionsCallback_: function(parent, data) { | |
672 var menuItem = this.createCallback_(parent, | |
673 data, | |
674 data.networkName, | |
675 'options'); | |
676 menuItem.style.backgroundImage = url(data.iconURL); | |
677 if (data.policyManaged) | |
678 menuItem.appendChild(new ManagedNetworkIndicator()); | |
679 if (data.connected || data.connecting) { | |
680 var label = menuItem.getElementsByClassName( | |
681 'network-menu-item-label')[0]; | |
682 label.classList.add('active-network'); | |
683 } | |
684 } | |
685 }; | |
686 | |
687 /** | |
688 * Creates a button-like control for configurating internet connectivity. | |
689 * @param {{key: string, | |
690 * subtitle: string, | |
691 * command: function} data Description of the network control. | |
692 * @constructor | |
693 */ | |
694 function NetworkButtonItem(data) { | |
695 var el = new NetworkListItem(data); | |
696 el.__proto__ = NetworkButtonItem.prototype; | |
697 el.decorate(); | |
698 return el; | |
699 } | |
700 | |
701 NetworkButtonItem.prototype = { | |
702 __proto__: NetworkListItem.prototype, | |
703 | |
704 /** @inheritDoc */ | |
705 decorate: function() { | |
706 if (this.data.subtitle) | |
707 this.subtitle = this.data.subtitle; | |
708 else | |
709 this.subtitle = null; | |
710 if (this.data.command) | |
711 this.addEventListener('click', this.data.command); | |
712 if (this.data.iconURL) | |
713 this.iconURL = this.data.iconURL; | |
714 else if (this.data.iconType) | |
715 this.iconType = this.data.iconType; | |
716 }, | |
717 }; | |
718 | |
719 /** | |
720 * A list of controls for manipulating network connectivity. | |
721 * @constructor | |
722 */ | |
723 var NetworkList = cr.ui.define('list'); | |
724 | |
725 NetworkList.prototype = { | |
726 __proto__: List.prototype, | |
727 | |
728 /** @inheritDoc */ | |
729 decorate: function() { | |
730 List.prototype.decorate.call(this); | |
731 this.startBatchUpdates(); | |
732 this.autoExpands = true; | |
733 this.addEventListener('blur', this.onBlur_); | |
734 this.dataModel = new ArrayDataModel([]); | |
735 | |
736 // Wi-Fi control is always visible. | |
737 this.update({key: 'wifi', networkList: []}); | |
738 | |
739 if (airplaneModeAvailable_()) { | |
740 this.update({key: 'airplaneMode', | |
741 subtitle: loadTimeData.getString('airplaneModeLabel'), | |
742 command: function() { | |
743 chrome.send('toggleAirplaneMode'); | |
744 }}); | |
745 } | |
746 | |
747 // Add connection control. | |
748 var addConnection = function(type) { | |
749 var callback = function() { | |
750 chrome.send('networkCommand', | |
751 [String(type), '?', 'connect']); | |
752 } | |
753 return callback; | |
754 } | |
755 this.update({key: 'addConnection', | |
756 iconType: 'add-connection', | |
757 menu: [{label: loadTimeData.getString('addConnectionWifi'), | |
758 command: addConnection(Constants.TYPE_WIFI)}, | |
759 {label: loadTimeData.getString('addConnectionVPN'), | |
760 command: addConnection(Constants.TYPE_VPN)}] | |
761 }); | |
762 | |
763 var prefs = options.Preferences.getInstance(); | |
764 prefs.addEventListener('cros.signed.data_roaming_enabled', | |
765 function(event) { | |
766 enableDataRoaming_ = event.value.value; | |
767 }); | |
768 this.endBatchUpdates(); | |
769 }, | |
770 | |
771 /** | |
772 * When the list loses focus, unselect all items in the list and close the | |
773 * active menu. | |
774 * @private | |
775 */ | |
776 onBlur_: function() { | |
777 this.selectionModel.unselectAll(); | |
778 closeMenu_(); | |
779 }, | |
780 | |
781 /** | |
782 * Finds the index of a network item within the data model based on | |
783 * category. | |
784 * @param {string} key Unique key for the item in the list. | |
785 * @return {number} The index of the network item, or |undefined| if it is | |
786 * not found. | |
787 */ | |
788 indexOf: function(key) { | |
789 var size = this.dataModel.length; | |
790 for (var i = 0; i < size; i++) { | |
791 var entry = this.dataModel.item(i); | |
792 if (entry.key == key) | |
793 return i; | |
794 } | |
795 }, | |
796 | |
797 /** | |
798 * Updates a network control. | |
799 * @param {Object.<string,string>} data Description of the entry. | |
800 */ | |
801 update: function(data) { | |
802 this.startBatchUpdates(); | |
803 var index = this.indexOf(data.key); | |
804 if (index == undefined) { | |
805 // Find reference position for adding the element. We cannot hide | |
806 // individual list elements, thus we need to conditionally add or | |
807 // remove elements and cannot rely on any element having a fixed index. | |
808 for (var i = 0; i < Constants.NETWORK_ORDER.length; i++) { | |
809 if (data.key == Constants.NETWORK_ORDER[i]) { | |
810 data.sortIndex = i; | |
811 break; | |
812 } | |
813 } | |
814 var referenceIndex = -1; | |
815 for (var i = 0; i < this.dataModel.length; i++) { | |
816 var entry = this.dataModel.item(i); | |
817 if (entry.sortIndex < data.sortIndex) | |
818 referenceIndex = i; | |
819 else | |
820 break; | |
821 } | |
822 if (referenceIndex == -1) { | |
823 // Prepend to the start of the list. | |
824 this.dataModel.splice(0, 0, data); | |
825 } else if (referenceIndex == this.dataModel.length) { | |
826 // Append to the end of the list. | |
827 this.dataModel.push(data); | |
828 } else { | |
829 // Insert after the reference element. | |
830 this.dataModel.splice(referenceIndex + 1, 0, data); | |
831 } | |
832 } else { | |
833 var entry = this.dataModel.item(index); | |
834 data.sortIndex = entry.sortIndex; | |
835 this.dataModel.splice(index, 1, data); | |
836 } | |
837 this.endBatchUpdates(); | |
838 }, | |
839 | |
840 /** @inheritDoc */ | |
841 createItem: function(entry) { | |
842 if (entry.networkList) | |
843 return new NetworkSelectorItem(entry); | |
844 if (entry.command) | |
845 return new NetworkButtonItem(entry); | |
846 if (entry.menu) | |
847 return new NetworkMenuItem(entry); | |
848 }, | |
849 | |
850 /** | |
851 * Deletes an element from the list. | |
852 * @param {string} key Unique identifier for the element. | |
853 */ | |
854 deleteItem: function(key) { | |
855 var index = this.indexOf(key); | |
856 if (index != undefined) | |
857 this.dataModel.splice(index, 1); | |
858 }, | |
859 | |
860 /** | |
861 * Updates the state of a toggle button. | |
862 * @param {string} key Unique identifier for the element. | |
863 * @param {boolean} active Whether the control is active. | |
864 */ | |
865 updateToggleControl: function(key, active) { | |
866 var index = this.indexOf(key); | |
867 if (index != undefined) { | |
868 var entry = this.dataModel.item(index); | |
869 entry.iconType = active ? 'control-active' : | |
870 'control-inactive'; | |
871 this.update(entry); | |
872 } | |
873 } | |
874 }; | |
875 | |
876 /** | |
877 * Chrome callback for updating network controls. | |
878 * @param {Object} data Description of available network devices and their | |
879 * corresponding state. | |
880 */ | |
881 NetworkList.refreshNetworkData = function(data) { | |
882 var networkList = $('network-list'); | |
883 networkList.startBatchUpdates(); | |
884 cellularAvailable_ = data.cellularAvailable; | |
885 cellularEnabled_ = data.cellularEnabled; | |
886 wimaxAvailable_ = data.wimaxAvailable; | |
887 wimaxEnabled_ = data.wimaxEnabled; | |
888 | |
889 if (data.accessLocked) { | |
890 $('network-locked-message').hidden = false; | |
891 networkList.disabled = true; | |
892 $('use-shared-proxies').disabled = true; | |
893 } else { | |
894 $('network-locked-message').hidden = true; | |
895 networkList.disabled = false; | |
896 $('use-shared-proxies').disabled = false; | |
897 } | |
898 | |
899 // Only show Ethernet control if connected. | |
900 var ethernetConnection = getConnection_(data.wiredList); | |
901 if (ethernetConnection) { | |
902 var type = String(Constants.TYPE_ETHERNET); | |
903 var path = ethernetConnection.servicePath; | |
904 var ethernetOptions = function() { | |
905 chrome.send('networkCommand', | |
906 [type, path, 'options']); | |
907 }; | |
908 networkList.update({key: 'ethernet', | |
909 subtitle: loadTimeData.getString('networkConnected'), | |
910 iconURL: ethernetConnection.iconURL, | |
911 command: ethernetOptions}); | |
912 } else { | |
913 networkList.deleteItem('ethernet'); | |
914 } | |
915 | |
916 if (data.wifiEnabled) { | |
917 loadData_('wifi', data.wirelessList, data.rememberedList); | |
918 } else { | |
919 var enableWifi = function() { | |
920 chrome.send('enableWifi'); | |
921 }; | |
922 networkList.update({key: 'wifi', | |
923 subtitle: loadTimeData.getString('networkDisabled'), | |
924 iconType: 'wifi', | |
925 command: enableWifi}); | |
926 } | |
927 | |
928 // Only show cellular control if available and not in airplane mode. | |
929 if (data.cellularAvailable && !data.airplaneMode) { | |
930 if (data.cellularEnabled) { | |
931 loadData_('cellular', data.wirelessList, data.rememberedList); | |
932 } else { | |
933 var subtitle = loadTimeData.getString('networkDisabled'); | |
934 var enableCellular = function() { | |
935 chrome.send('enableCellular'); | |
936 }; | |
937 networkList.update({key: 'cellular', | |
938 subtitle: subtitle, | |
939 iconType: 'cellular', | |
940 command: enableCellular}); | |
941 } | |
942 } else { | |
943 networkList.deleteItem('cellular'); | |
944 } | |
945 | |
946 // Only show cellular control if available and not in airplane mode. | |
947 if (data.wimaxAvailable && !data.airplaneMode) { | |
948 if (data.wimaxEnabled) { | |
949 loadData_('wimax', data.wirelessList, data.rememberedList); | |
950 } else { | |
951 var subtitle = loadTimeData.getString('networkDisabled'); | |
952 var enableWimax = function() { | |
953 chrome.send('enableWimax'); | |
954 }; | |
955 networkList.update({key: 'wimax', | |
956 subtitle: subtitle, | |
957 iconType: 'cellular', | |
958 command: enableWimax}); | |
959 } | |
960 } else { | |
961 networkList.deleteItem('wimax'); | |
962 } | |
963 | |
964 // Only show VPN control if there is an available network and an internet | |
965 // connection. | |
966 if (data.vpnList.length > 0 && (ethernetConnection || | |
967 isConnected_(data.wirelessList))) | |
968 loadData_('vpn', data.vpnList, data.rememberedList); | |
969 else | |
970 networkList.deleteItem('vpn'); | |
971 networkList.updateToggleControl('airplaneMode', data.airplaneMode); | |
972 networkList.endBatchUpdates(); | |
973 }; | |
974 | |
975 /** | |
976 * Element for indicating a policy managed network. | |
977 * @constructor | |
978 */ | |
979 function ManagedNetworkIndicator() { | |
980 var el = cr.doc.createElement('div'); | |
981 el.__proto__ = ManagedNetworkIndicator.prototype; | |
982 el.decorate(); | |
983 return el; | |
984 } | |
985 | |
986 ManagedNetworkIndicator.prototype = { | |
987 __proto__: ControlledSettingIndicator.prototype, | |
988 | |
989 /** @inheritDoc */ | |
990 decorate: function() { | |
991 ControlledSettingIndicator.prototype.decorate.call(this); | |
992 this.controlledBy = 'policy'; | |
993 var policyLabel = loadTimeData.getString('managedNetwork'); | |
994 this.setAttribute('textPolicy', policyLabel); | |
995 this.className = 'controlled-setting-indicator'; | |
996 // The default popup clips to the bounds of the list of networks in the | |
997 // drop-down because it has enforced size constraints with auto- | |
998 // scrolling. Use a tooltip in place of the bubble popup until the | |
999 // clipping issues are resolved. | |
1000 this.setAttribute('title', policyLabel); | |
1001 this.addEventListener('click', function(e) { | |
1002 e.preventDefault(); | |
1003 e.stopPropagation(); | |
1004 }); | |
1005 } | |
1006 }; | |
1007 | |
1008 /** | |
1009 * Updates the list of available networks and their status, filtered by | |
1010 * network type. | |
1011 * @param {string} category The type of network. | |
1012 * @param {Array} available The list of available networks and their status. | |
1013 * @param {Array} remembered The list of remmebered networks. | |
1014 */ | |
1015 function loadData_(category, available, remembered) { | |
1016 var data = {key: category}; | |
1017 var type = categoryMap[category]; | |
1018 var availableNetworks = []; | |
1019 for (var i = 0; i < available.length; i++) { | |
1020 if (available[i].networkType == type) | |
1021 availableNetworks.push(available[i]); | |
1022 } | |
1023 data.networkList = availableNetworks; | |
1024 if (remembered) { | |
1025 var rememberedNetworks = []; | |
1026 for (var i = 0; i < remembered.length; i++) { | |
1027 if (remembered[i].networkType == type) | |
1028 rememberedNetworks.push(remembered[i]); | |
1029 } | |
1030 data.rememberedNetworks = rememberedNetworks; | |
1031 } | |
1032 $('network-list').update(data); | |
1033 } | |
1034 | |
1035 /** | |
1036 * Hides the currently visible menu. | |
1037 * @private | |
1038 */ | |
1039 function closeMenu_() { | |
1040 if (activeMenu_) { | |
1041 var menu = $(activeMenu_); | |
1042 menu.hidden = true; | |
1043 if (menu.data && menu.data.discardOnClose) | |
1044 menu.parentNode.removeChild(menu); | |
1045 activeMenu_ = null; | |
1046 } | |
1047 } | |
1048 | |
1049 /** | |
1050 * Determines if the user is connected to or in the process of connecting to | |
1051 * a wireless network. | |
1052 * @param {Array.<Object>} networkList List of networks. | |
1053 * @return {boolean} True if connected or connecting to a network. | |
1054 * @private | |
1055 */ | |
1056 function isConnected_(networkList) { | |
1057 return getConnection_(networkList) != null; | |
1058 } | |
1059 | |
1060 /** | |
1061 * Fetches the active connection. | |
1062 * @param {Array.<Object>} networkList List of networks. | |
1063 * @return {boolean} True if connected or connecting to a network. | |
1064 * @private | |
1065 */ | |
1066 function getConnection_(networkList) { | |
1067 if (!networkList) | |
1068 return null; | |
1069 for (var i = 0; i < networkList.length; i++) { | |
1070 var entry = networkList[i]; | |
1071 if (entry.connected || entry.connecting) | |
1072 return entry; | |
1073 } | |
1074 return null; | |
1075 } | |
1076 | |
1077 /** | |
1078 * Queries if airplane mode is available. | |
1079 * @return {boolean} Indicates if airplane mode is available. | |
1080 * @private | |
1081 */ | |
1082 function airplaneModeAvailable_() { | |
1083 // TODO(kevers): Use library callback to determine if airplane mode is | |
1084 // available once back-end suport is in place. | |
1085 return false; | |
1086 } | |
1087 | |
1088 /** | |
1089 * Whether the Network list is disabled. Only used for display purpose. | |
1090 * @type {boolean} | |
1091 */ | |
1092 cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR); | |
1093 | |
1094 // Export | |
1095 return { | |
1096 NetworkList: NetworkList | |
1097 }; | |
1098 }); | |
OLD | NEW |