Special case in users/sync section for supervised users
[chromium-blink-merge.git] / chrome / browser / resources / options / browser_options.js
blob7b8e03f23e8c098250107fa0569f21098d920e8e
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', function() {
6   var OptionsPage = options.OptionsPage;
7   var ArrayDataModel = cr.ui.ArrayDataModel;
8   var RepeatingButton = cr.ui.RepeatingButton;
10   //
11   // BrowserOptions class
12   // Encapsulated handling of browser options page.
13   //
14   function BrowserOptions() {
15     OptionsPage.call(this, 'settings', loadTimeData.getString('settingsTitle'),
16                      'settings');
17   }
19   cr.addSingletonGetter(BrowserOptions);
21   BrowserOptions.prototype = {
22     __proto__: options.OptionsPage.prototype,
24     /**
25      * Keeps track of whether the user is signed in or not.
26      * @type {boolean}
27      * @private
28      */
29     signedIn_: false,
31     /**
32      * Keeps track of whether the user has completed sync setup or not.
33      * @type {boolean}
34      * @private
35      */
36     setupCompleted_: false,
38     /**
39      * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
40      * |onShowHomeButtonChanged_|.
41      * @type {boolean}
42      * @private
43      */
44     onShowHomeButtonChangedCalled_: false,
46     /**
47      * Track if page initialization is complete.  All C++ UI handlers have the
48      * chance to manipulate page content within their InitializePage methods.
49      * This flag is set to true after all initializers have been called.
50      * @type {boolean}
51      * @private
52      */
53     initializationComplete_: false,
55     /** @override */
56     initializePage: function() {
57       OptionsPage.prototype.initializePage.call(this);
58       var self = this;
60       // Ensure that navigation events are unblocked on uber page. A reload of
61       // the settings page while an overlay is open would otherwise leave uber
62       // page in a blocked state, where tab switching is not possible.
63       uber.invokeMethodOnParent('stopInterceptingEvents');
65       window.addEventListener('message', this.handleWindowMessage_.bind(this));
67       $('advanced-settings-expander').onclick = function() {
68         self.toggleSectionWithAnimation_(
69             $('advanced-settings'),
70             $('advanced-settings-container'));
72         // If the link was focused (i.e., it was activated using the keyboard)
73         // and it was used to show the section (rather than hiding it), focus
74         // the first element in the container.
75         if (document.activeElement === $('advanced-settings-expander') &&
76                 $('advanced-settings').style.height === '') {
77           var focusElement = $('advanced-settings-container').querySelector(
78               'button, input, list, select, a[href]');
79           if (focusElement)
80             focusElement.focus();
81         }
82       }
84       $('advanced-settings').addEventListener('webkitTransitionEnd',
85           this.updateAdvancedSettingsExpander_.bind(this));
87       if (cr.isChromeOS)
88         UIAccountTweaks.applyGuestModeVisibility(document);
90       // Sync (Sign in) section.
91       this.updateSyncState_(loadTimeData.getValue('syncData'));
93       $('start-stop-sync').onclick = function(event) {
94         if (self.signedIn_)
95           SyncSetupOverlay.showStopSyncingUI();
96         else if (cr.isChromeOS)
97           SyncSetupOverlay.showSetupUI();
98         else
99           SyncSetupOverlay.startSignIn();
100       };
101       $('customize-sync').onclick = function(event) {
102         SyncSetupOverlay.showSetupUI();
103       };
105       // Internet connection section (ChromeOS only).
106       if (cr.isChromeOS) {
107         options.network.NetworkList.decorate($('network-list'));
108         options.network.NetworkList.refreshNetworkData(
109             loadTimeData.getValue('networkData'));
110       }
112       // On Startup section.
113       Preferences.getInstance().addEventListener('session.restore_on_startup',
114           this.onRestoreOnStartupChanged_.bind(this));
115       Preferences.getInstance().addEventListener(
116           'session.urls_to_restore_on_startup',
117           function(event) {
118             $('startup-set-pages').disabled = event.value.disabled;
119           });
121       $('startup-set-pages').onclick = function() {
122         OptionsPage.navigateToPage('startup');
123       };
125       // Appearance section.
126       Preferences.getInstance().addEventListener('browser.show_home_button',
127           this.onShowHomeButtonChanged_.bind(this));
129       Preferences.getInstance().addEventListener('homepage',
130           this.onHomePageChanged_.bind(this));
131       Preferences.getInstance().addEventListener('homepage_is_newtabpage',
132           this.onHomePageIsNtpChanged_.bind(this));
134       $('change-home-page').onclick = function(event) {
135         OptionsPage.navigateToPage('homePageOverlay');
136       };
138       if ($('set-wallpaper')) {
139         $('set-wallpaper').onclick = function(event) {
140           chrome.send('openWallpaperManager');
141         };
142       }
144       $('themes-gallery').onclick = function(event) {
145         window.open(loadTimeData.getString('themesGalleryURL'));
146       };
147       $('themes-reset').onclick = function(event) {
148         chrome.send('themesReset');
149       };
151       if (loadTimeData.getBoolean('profileIsManaged')) {
152         if ($('themes-GTK-button'))
153           $('themes-GTK-button').disabled = true;
154         $('themes-reset').disabled = true;
155         $('themes-gallery').disabled = true;
156       }
158       // Device section (ChromeOS only).
159       if (cr.isChromeOS) {
160         $('keyboard-settings-button').onclick = function(evt) {
161           OptionsPage.navigateToPage('keyboard-overlay');
162         };
163         $('pointer-settings-button').onclick = function(evt) {
164           OptionsPage.navigateToPage('pointer-overlay');
165         };
166       }
168       // Search section.
169       $('manage-default-search-engines').onclick = function(event) {
170         OptionsPage.navigateToPage('searchEngines');
171         chrome.send('coreOptionsUserMetricsAction',
172                     ['Options_ManageSearchEngines']);
173       };
174       $('default-search-engine').addEventListener('change',
175           this.setDefaultSearchEngine_);
177       // Users section.
178       if (loadTimeData.valueExists('profilesInfo')) {
179         $('profiles-section').hidden = false;
181         var profilesList = $('profiles-list');
182         options.browser_options.ProfileList.decorate(profilesList);
183         profilesList.autoExpands = true;
185         this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
187         profilesList.addEventListener('change',
188             this.setProfileViewButtonsStatus_);
189         $('profiles-create').onclick = function(event) {
190           ManageProfileOverlay.showCreateDialog();
191         };
192         if (OptionsPage.isSettingsApp()) {
193           $('profiles-app-list-switch').onclick = function(event) {
194             var selectedProfile = self.getSelectedProfileItem_();
195             chrome.send('switchAppListProfile', [selectedProfile.filePath]);
196           };
197         }
198         $('profiles-manage').onclick = function(event) {
199           ManageProfileOverlay.showManageDialog();
200         };
201         $('profiles-delete').onclick = function(event) {
202           var selectedProfile = self.getSelectedProfileItem_();
203           if (selectedProfile)
204             ManageProfileOverlay.showDeleteDialog(selectedProfile);
205         };
206         if (loadTimeData.getBoolean('profileIsManaged')) {
207           $('profiles-create').disabled = true;
208           $('profiles-delete').disabled = true;
209           $('profiles-list').canDeleteItems = false;
210         }
211       }
213       if (cr.isChromeOS) {
214         if (!UIAccountTweaks.loggedInAsGuest()) {
215           $('account-picture-wrapper').onclick = function(event) {
216             OptionsPage.navigateToPage('changePicture');
217           };
218         }
220         // Username (canonical email) of the currently logged in user or
221         // |kGuestUser| if a guest session is active.
222         this.username_ = loadTimeData.getString('username');
224         this.updateAccountPicture_();
226         $('account-picture-wrapper').oncontextmenu = function(e) {
227           e.preventDefault();
228         };
230         $('manage-accounts-button').onclick = function(event) {
231           OptionsPage.navigateToPage('accounts');
232           chrome.send('coreOptionsUserMetricsAction',
233               ['Options_ManageAccounts']);
234         };
235       } else {
236         $('import-data').onclick = function(event) {
237           ImportDataOverlay.show();
238           chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
239         };
241         if ($('themes-native-button')) {
242           $('themes-native-button').onclick = function(event) {
243             chrome.send('themesSetNative');
244           };
245         }
246       }
248       // Default browser section.
249       if (!cr.isChromeOS) {
250         $('set-as-default-browser').onclick = function(event) {
251           chrome.send('becomeDefaultBrowser');
252         };
254         $('auto-launch').onclick = this.handleAutoLaunchChanged_;
255       }
257       // Privacy section.
258       $('privacyContentSettingsButton').onclick = function(event) {
259         OptionsPage.navigateToPage('content');
260         OptionsPage.showTab($('cookies-nav-tab'));
261         chrome.send('coreOptionsUserMetricsAction',
262             ['Options_ContentSettings']);
263       };
264       $('privacyClearDataButton').onclick = function(event) {
265         OptionsPage.navigateToPage('clearBrowserData');
266         chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
267       };
268       $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
269       // 'metricsReportingEnabled' element is only present on Chrome branded
270       // builds.
271       if ($('metricsReportingEnabled')) {
272         $('metricsReportingEnabled').onclick = function(event) {
273           chrome.send('metricsReportingCheckboxAction',
274               [String(event.currentTarget.checked)]);
275         };
276       }
278       // Bluetooth (CrOS only).
279       if (cr.isChromeOS) {
280         options.system.bluetooth.BluetoothDeviceList.decorate(
281             $('bluetooth-paired-devices-list'));
283         $('bluetooth-add-device').onclick =
284             this.handleAddBluetoothDevice_.bind(this);
286         $('enable-bluetooth').onchange = function(event) {
287           var state = $('enable-bluetooth').checked;
288           chrome.send('bluetoothEnableChange', [Boolean(state)]);
289         };
291         $('bluetooth-reconnect-device').onclick = function(event) {
292           var device = $('bluetooth-paired-devices-list').selectedItem;
293           var address = device.address;
294           chrome.send('updateBluetoothDevice', [address, 'connect']);
295           OptionsPage.closeOverlay();
296         };
298         $('bluetooth-reconnect-device').onmousedown = function(event) {
299           // Prevent 'blur' event, which would reset the list selection,
300           // thereby disabling the apply button.
301           event.preventDefault();
302         };
304         $('bluetooth-paired-devices-list').addEventListener('change',
305             function() {
306           var item = $('bluetooth-paired-devices-list').selectedItem;
307           var disabled = !item || item.connected || !item.connectable;
308           $('bluetooth-reconnect-device').disabled = disabled;
309         });
310       }
312       // Passwords and Forms section.
313       $('autofill-settings').onclick = function(event) {
314         OptionsPage.navigateToPage('autofill');
315         chrome.send('coreOptionsUserMetricsAction',
316             ['Options_ShowAutofillSettings']);
317       };
318       $('manage-passwords').onclick = function(event) {
319         OptionsPage.navigateToPage('passwords');
320         OptionsPage.showTab($('passwords-nav-tab'));
321         chrome.send('coreOptionsUserMetricsAction',
322             ['Options_ShowPasswordManager']);
323       };
324       if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
325         // Disable and turn off Autofill in guest mode.
326         var autofillEnabled = $('autofill-enabled');
327         autofillEnabled.disabled = true;
328         autofillEnabled.checked = false;
329         cr.dispatchSimpleEvent(autofillEnabled, 'change');
330         $('autofill-settings').disabled = true;
332         // Disable and turn off Password Manager in guest mode.
333         var passwordManagerEnabled = $('password-manager-enabled');
334         passwordManagerEnabled.disabled = true;
335         passwordManagerEnabled.checked = false;
336         cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
337         $('manage-passwords').disabled = true;
338       }
340       if (cr.isMac) {
341         $('mac-passwords-warning').hidden =
342             !loadTimeData.getBoolean('multiple_profiles');
343       }
345       // Network section.
346       if (!cr.isChromeOS) {
347         $('proxiesConfigureButton').onclick = function(event) {
348           chrome.send('showNetworkProxySettings');
349         };
350       }
352       // Web Content section.
353       $('fontSettingsCustomizeFontsButton').onclick = function(event) {
354         OptionsPage.navigateToPage('fonts');
355         chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
356       };
357       $('defaultFontSize').onchange = function(event) {
358         var value = event.target.options[event.target.selectedIndex].value;
359         Preferences.setIntegerPref(
360              'webkit.webprefs.default_fixed_font_size',
361              value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
362         chrome.send('defaultFontSizeAction', [String(value)]);
363       };
364       $('defaultZoomFactor').onchange = function(event) {
365         chrome.send('defaultZoomFactorAction',
366             [String(event.target.options[event.target.selectedIndex].value)]);
367       };
369       // Languages section.
370       var showLanguageOptions = function(event) {
371         OptionsPage.navigateToPage('languages');
372         chrome.send('coreOptionsUserMetricsAction',
373             ['Options_LanuageAndSpellCheckSettings']);
374       };
375       $('language-button').onclick = showLanguageOptions;
376       $('manage-languages').onclick = showLanguageOptions;
378       // Downloads section.
379       Preferences.getInstance().addEventListener('download.default_directory',
380           this.onDefaultDownloadDirectoryChanged_.bind(this));
381       $('downloadLocationChangeButton').onclick = function(event) {
382         chrome.send('selectDownloadLocation');
383       };
384       if (!cr.isChromeOS) {
385         $('autoOpenFileTypesResetToDefault').onclick = function(event) {
386           chrome.send('autoOpenFileTypesAction');
387         };
388       } else {
389         $('disable-drive-row').hidden =
390             UIAccountTweaks.loggedInAsLocallyManagedUser();
391       }
393       // HTTPS/SSL section.
394       if (cr.isWindows || cr.isMac) {
395         $('certificatesManageButton').onclick = function(event) {
396           chrome.send('showManageSSLCertificates');
397         };
398       } else {
399         $('certificatesManageButton').onclick = function(event) {
400           OptionsPage.navigateToPage('certificates');
401           chrome.send('coreOptionsUserMetricsAction',
402                       ['Options_ManageSSLCertificates']);
403         };
404       }
406       // Cloud Print section.
407       // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
408       // certain platforms, or could be enabled by a lab.
409       if (!cr.isChromeOS) {
410         $('cloudPrintConnectorSetupButton').onclick = function(event) {
411           if ($('cloudPrintManageButton').style.display == 'none') {
412             // Disable the button, set its text to the intermediate state.
413             $('cloudPrintConnectorSetupButton').textContent =
414               loadTimeData.getString('cloudPrintConnectorEnablingButton');
415             $('cloudPrintConnectorSetupButton').disabled = true;
416             chrome.send('showCloudPrintSetupDialog');
417           } else {
418             chrome.send('disableCloudPrintConnector');
419           }
420         };
421       }
422       $('cloudPrintManageButton').onclick = function(event) {
423         chrome.send('showCloudPrintManagePage');
424       };
426       // Accessibility section (CrOS only).
427       if (cr.isChromeOS) {
428         $('accessibility-spoken-feedback-check').onchange = function(event) {
429           chrome.send('spokenFeedbackChange',
430                       [$('accessibility-spoken-feedback-check').checked]);
431         };
433         $('accessibility-high-contrast-check').onchange = function(event) {
434           chrome.send('highContrastChange',
435                       [$('accessibility-high-contrast-check').checked]);
436         };
437       }
439       // Display management section (CrOS only).
440       if (cr.isChromeOS) {
441         $('display-options').onclick = function(event) {
442           OptionsPage.navigateToPage('display');
443           chrome.send('coreOptionsUserMetricsAction',
444                       ['Options_Display']);
445         };
446       }
448       // Factory reset section (CrOS only).
449       if (cr.isChromeOS) {
450         $('factory-reset-restart').onclick = function(event) {
451           OptionsPage.navigateToPage('factoryResetData');
452         };
453       }
455       // System section.
456       if (!cr.isChromeOS) {
457         var updateGpuRestartButton = function() {
458           $('gpu-mode-reset-restart').hidden =
459               loadTimeData.getBoolean('gpuEnabledAtStart') ==
460               $('gpu-mode-checkbox').checked;
461         };
462         Preferences.getInstance().addEventListener(
463             $('gpu-mode-checkbox').getAttribute('pref'),
464             updateGpuRestartButton);
465         $('gpu-mode-reset-restart-button').onclick = function(event) {
466           chrome.send('restartBrowser');
467         };
468         updateGpuRestartButton();
469       }
471       // Reset profile settings section.
472       $('reset-profile-settings-section').hidden =
473           !loadTimeData.getValue('enableResetProfileSettingsSection');
474       $('reset-profile-settings').onclick = function(event) {
475         OptionsPage.navigateToPage('resetProfileSettings');
476       };
477     },
479     /** @override */
480     didShowPage: function() {
481       $('search-field').focus();
482     },
484    /**
485     * Called after all C++ UI handlers have called InitializePage to notify
486     * that initialization is complete.
487     * @private
488     */
489     notifyInitializationComplete_: function() {
490       this.initializationComplete_ = true;
491       cr.dispatchSimpleEvent(document, 'initializationComplete');
492     },
494     /**
495      * Event listener for the 'session.restore_on_startup' pref.
496      * @param {Event} event The preference change event.
497      * @private
498      */
499     onRestoreOnStartupChanged_: function(event) {
500       /** @const */ var showHomePageValue = 0;
502       if (event.value.value == showHomePageValue) {
503         // If the user previously selected "Show the homepage", the
504         // preference will already be migrated to "Open a specific page". So
505         // the only way to reach this code is if the 'restore on startup'
506         // preference is managed.
507         assert(event.value.controlledBy);
509         // Select "open the following pages" and lock down the list of URLs
510         // to reflect the intention of the policy.
511         $('startup-show-pages').checked = true;
512         StartupOverlay.getInstance().setControlsDisabled(true);
513       } else {
514         // Re-enable the controls in the startup overlay if necessary.
515         StartupOverlay.getInstance().updateControlStates();
516       }
517     },
519     /**
520      * Handler for messages sent from the main uber page.
521      * @param {Event} e The 'message' event from the uber page.
522      * @private
523      */
524     handleWindowMessage_: function(e) {
525       if (e.data.method == 'frameSelected')
526         $('search-field').focus();
527     },
529     /**
530      * Shows the given section.
531      * @param {HTMLElement} section The section to be shown.
532      * @param {HTMLElement} container The container for the section. Must be
533      *     inside of |section|.
534      * @param {boolean} animate Indicate if the expansion should be animated.
535      * @private
536      */
537     showSection_: function(section, container, animate) {
538       if (animate)
539         this.addTransitionEndListener_(section);
541       // Unhide
542       section.hidden = false;
544       var expander = function() {
545         // Reveal the section using a WebKit transition if animating.
546         if (animate) {
547           section.classList.add('sliding');
548           section.style.height = container.offsetHeight + 'px';
549         } else {
550           section.style.height = 'auto';
551         }
552       };
554       // Delay starting the transition if animating so that hidden change will
555       // be processed.
556       if (animate)
557         setTimeout(expander, 0);
558       else
559         expander();
560       },
562     /**
563      * Shows the given section, with animation.
564      * @param {HTMLElement} section The section to be shown.
565      * @param {HTMLElement} container The container for the section. Must be
566      *     inside of |section|.
567      * @private
568      */
569     showSectionWithAnimation_: function(section, container) {
570       this.showSection_(section, container, /*animate */ true);
571     },
573     /**
574      * See showSectionWithAnimation_.
575      */
576     hideSectionWithAnimation_: function(section, container) {
577       this.addTransitionEndListener_(section);
579       // Before we start hiding the section, we need to set
580       // the height to a pixel value.
581       section.style.height = container.offsetHeight + 'px';
583       // Delay starting the transition so that the height change will be
584       // processed.
585       setTimeout(function() {
586         // Hide the section using a WebKit transition.
587         section.classList.add('sliding');
588         section.style.height = '';
589       }, 0);
590     },
592     /**
593      * See showSectionWithAnimation_.
594      */
595     toggleSectionWithAnimation_: function(section, container) {
596       if (section.style.height == '')
597         this.showSectionWithAnimation_(section, container);
598       else
599         this.hideSectionWithAnimation_(section, container);
600     },
602     /**
603      * Scrolls the settings page to make the section visible auto-expanding
604      * advanced settings if required.  The transition is not animated.  This
605      * method is used to ensure that a section associated with an overlay
606      * is visible when the overlay is closed.
607      * @param {!Element} section  The section to make visible.
608      * @private
609      */
610     scrollToSection_: function(section) {
611       var advancedSettings = $('advanced-settings');
612       var container = $('advanced-settings-container');
613       if (advancedSettings.hidden && section.parentNode == container) {
614         this.showSection_($('advanced-settings'),
615                           $('advanced-settings-container'),
616                           /* animate */ false);
617         this.updateAdvancedSettingsExpander_();
618       }
620       if (!this.initializationComplete_) {
621         var self = this;
622         var callback = function() {
623            document.removeEventListener('initializationComplete', callback);
624            self.scrollToSection_(section);
625         };
626         document.addEventListener('initializationComplete', callback);
627         return;
628       }
630       var pageContainer = $('page-container');
631       var pageTop = parseFloat(pageContainer.style.top);
632       var topSection = document.querySelector('#page-container section');
633       var pageHeight = document.body.scrollHeight - topSection.offsetTop;
634       var sectionTop = section.offsetTop;
635       var sectionHeight = section.offsetHeight;
636       var marginBottom = window.getComputedStyle(section).marginBottom;
637       if (marginBottom)
638         sectionHeight += parseFloat(marginBottom);
639       if (pageHeight - pageTop < sectionTop + sectionHeight) {
640         pageContainer.oldScrollTop = sectionTop + sectionHeight - pageHeight;
641         var verticalPosition = pageContainer.getBoundingClientRect().top -
642             pageContainer.oldScrollTop;
643         pageContainer.style.top = verticalPosition + 'px';
644       }
645     },
647     /**
648      * Adds a |webkitTransitionEnd| listener to the given section so that
649      * it can be animated. The listener will only be added to a given section
650      * once, so this can be called as multiple times.
651      * @param {HTMLElement} section The section to be animated.
652      * @private
653      */
654     addTransitionEndListener_: function(section) {
655       if (section.hasTransitionEndListener_)
656         return;
658       section.addEventListener('webkitTransitionEnd',
659           this.onTransitionEnd_.bind(this));
660       section.hasTransitionEndListener_ = true;
661     },
663     /**
664      * Called after an animation transition has ended.
665      * @private
666      */
667     onTransitionEnd_: function(event) {
668       if (event.propertyName != 'height')
669         return;
671       var section = event.target;
673       // Disable WebKit transitions.
674       section.classList.remove('sliding');
676       if (section.style.height == '') {
677         // Hide the content so it can't get tab focus.
678         section.hidden = true;
679       } else {
680         // Set the section height to 'auto' to allow for size changes
681         // (due to font change or dynamic content).
682         section.style.height = 'auto';
683       }
684     },
686     updateAdvancedSettingsExpander_: function() {
687       var expander = $('advanced-settings-expander');
688       if ($('advanced-settings').style.height == '')
689         expander.textContent = loadTimeData.getString('showAdvancedSettings');
690       else
691         expander.textContent = loadTimeData.getString('hideAdvancedSettings');
692     },
694     /**
695      * Updates the sync section with the given state.
696      * @param {Object} syncData A bunch of data records that describe the status
697      *     of the sync system.
698      * @private
699      */
700     updateSyncState_: function(syncData) {
701       if (!syncData.signinAllowed && !syncData.supervisedUser) {
702         $('sync-section').hidden = true;
703         return;
704       }
706       $('sync-section').hidden = false;
708       var subSection = $('sync-section').firstChild;
709       while (subSection) {
710         if (subSection.nodeType == Node.ELEMENT_NODE)
711           subSection.hidden = syncData.supervisedUser;
712         subSection = subSection.nextSibling;
713       }
715       if (syncData.supervisedUser) {
716         $('account-picture-wrapper').hidden = false;
717         $('sync-general').hidden = false;
718         $('sync-status').hidden = true;
719         return;
720       }
721       // If the user gets signed out or if sync gets disabled while the advanced
722       // sync settings dialog is visible, say, due to a dashboard clear, close
723       // the dialog.
724       if ((this.signedIn_ && !syncData.signedIn) ||
725           (this.setupCompleted_ && !syncData.setupCompleted)) {
726         SyncSetupOverlay.closeOverlay();
727       }
729       this.signedIn_ = syncData.signedIn;
730       this.setupCompleted_ = syncData.setupCompleted;
732       // Display the "advanced settings" button if we're signed in and sync is
733       // not managed/disabled. If the user is signed in, but sync is disabled,
734       // this button is used to re-enable sync.
735       var customizeSyncButton = $('customize-sync');
736       customizeSyncButton.hidden = !this.signedIn_ ||
737                                    syncData.managed ||
738                                    !syncData.syncSystemEnabled;
739       customizeSyncButton.textContent = syncData.setupCompleted ?
740           loadTimeData.getString('customizeSync') :
741           loadTimeData.getString('syncButtonTextStart');
743       // Disable the "sign in" button if we're currently signing in, or if we're
744       // already signed in and signout is not allowed.
745       var signInButton = $('start-stop-sync');
746       signInButton.disabled = syncData.setupInProgress ||
747                               !syncData.signoutAllowed;
748       if (!syncData.signoutAllowed)
749         $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
750       else
751         $('start-stop-sync-indicator').removeAttribute('controlled-by');
753       // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome.
754       signInButton.hidden = cr.isChromeOS;
756       signInButton.textContent =
757           this.signedIn_ ?
758               loadTimeData.getString('syncButtonTextStop') :
759               syncData.setupInProgress ?
760                   loadTimeData.getString('syncButtonTextInProgress') :
761                   loadTimeData.getString('syncButtonTextSignIn');
762       $('start-stop-sync-indicator').hidden = signInButton.hidden;
764       // TODO(estade): can this just be textContent?
765       $('sync-status-text').innerHTML = syncData.statusText;
766       var statusSet = syncData.statusText.length != 0;
767       $('sync-overview').hidden = statusSet;
768       $('sync-status').hidden = !statusSet;
770       $('sync-action-link').textContent = syncData.actionLinkText;
771       // Don't show the action link if it is empty or undefined.
772       $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
773       $('sync-action-link').disabled = syncData.managed ||
774                                        !syncData.syncSystemEnabled;
776       // On Chrome OS, sign out the user and sign in again to get fresh
777       // credentials on auth errors.
778       $('sync-action-link').onclick = function(event) {
779         if (cr.isChromeOS && syncData.hasError)
780           SyncSetupOverlay.doSignOutOnAuthError();
781         else
782           SyncSetupOverlay.showErrorUI();
783       };
785       if (syncData.hasError)
786         $('sync-status').classList.add('sync-error');
787       else
788         $('sync-status').classList.remove('sync-error');
790       customizeSyncButton.disabled = syncData.hasUnrecoverableError;
791       // Move #enable-auto-login-checkbox to a different location on CrOS.
792       if (cr.isChromeOs) {
793         $('sync-general').insertBefore($('sync-status').nextSibling,
794                                        $('enable-auto-login-checkbox'));
795       }
796       $('enable-auto-login-checkbox').hidden = !syncData.autoLoginVisible;
797     },
799     /**
800      * Get the start/stop sync button DOM element. Used for testing.
801      * @return {DOMElement} The start/stop sync button.
802      * @private
803      */
804     getStartStopSyncButton_: function() {
805       return $('start-stop-sync');
806     },
808     /**
809      * Event listener for the 'show home button' preference. Shows/hides the
810      * UI for changing the home page with animation, unless this is the first
811      * time this function is called, in which case there is no animation.
812      * @param {Event} event The preference change event.
813      */
814     onShowHomeButtonChanged_: function(event) {
815       var section = $('change-home-page-section');
816       if (this.onShowHomeButtonChangedCalled_) {
817         var container = $('change-home-page-section-container');
818         if (event.value.value)
819           this.showSectionWithAnimation_(section, container);
820         else
821           this.hideSectionWithAnimation_(section, container);
822       } else {
823         section.hidden = !event.value.value;
824         this.onShowHomeButtonChangedCalled_ = true;
825       }
826     },
828     /**
829      * Event listener for the 'homepage is NTP' preference. Updates the label
830      * next to the 'Change' button.
831      * @param {Event} event The preference change event.
832      */
833     onHomePageIsNtpChanged_: function(event) {
834       if (!event.value.uncommitted) {
835         $('home-page-url').hidden = event.value.value;
836         $('home-page-ntp').hidden = !event.value.value;
837       }
838     },
840     /**
841      * Event listener for changes to the homepage preference. Updates the label
842      * next to the 'Change' button.
843      * @param {Event} event The preference change event.
844      */
845     onHomePageChanged_: function(event) {
846       if (!event.value.uncommitted)
847         $('home-page-url').textContent = this.stripHttp_(event.value.value);
848     },
850     /**
851      * Removes the 'http://' from a URL, like the omnibox does. If the string
852      * doesn't start with 'http://' it is returned unchanged.
853      * @param {string} url The url to be processed
854      * @return {string} The url with the 'http://' removed.
855      */
856     stripHttp_: function(url) {
857       return url.replace(/^http:\/\//, '');
858     },
860    /**
861     * Shows the autoLaunch preference and initializes its checkbox value.
862     * @param {bool} enabled Whether autolaunch is enabled or or not.
863     * @private
864     */
865     updateAutoLaunchState_: function(enabled) {
866       $('auto-launch-option').hidden = false;
867       $('auto-launch').checked = enabled;
868     },
870     /**
871      * Called when the value of the download.default_directory preference
872      * changes.
873      * @param {Event} event Change event.
874      * @private
875      */
876     onDefaultDownloadDirectoryChanged_: function(event) {
877       $('downloadLocationPath').value = event.value.value;
878       if (cr.isChromeOS) {
879         // On ChromeOS, replace /special/drive with Drive for drive paths, and
880         // /home/chronos/user/Downloads with Downloads for local files.
881         // Also replace '/' with ' \u203a ' (angled quote sign) everywhere.
882         var path = $('downloadLocationPath').value;
883         path = path.replace(/^\/special\/drive\/root/, 'Google Drive');
884         path = path.replace(/^\/home\/chronos\/user\//, '');
885         path = path.replace(/\//g, ' \u203a ');
886         $('downloadLocationPath').value = path;
887       }
888       if (event.value.disabled)
889         $('download-location-label').classList.add('disabled');
890       else
891         $('download-location-label').classList.remove('disabled');
892       $('downloadLocationChangeButton').disabled = event.value.disabled;
893     },
895     /**
896      * Update the Default Browsers section based on the current state.
897      * @param {string} statusString Description of the current default state.
898      * @param {boolean} isDefault Whether or not the browser is currently
899      *     default.
900      * @param {boolean} canBeDefault Whether or not the browser can be default.
901      * @private
902      */
903     updateDefaultBrowserState_: function(statusString, isDefault,
904                                          canBeDefault) {
905       if (!cr.isChromeOS) {
906         var label = $('default-browser-state');
907         label.textContent = statusString;
909         $('set-as-default-browser').hidden = !canBeDefault || isDefault;
910       }
911     },
913     /**
914      * Clears the search engine popup.
915      * @private
916      */
917     clearSearchEngines_: function() {
918       $('default-search-engine').textContent = '';
919     },
921     /**
922      * Updates the search engine popup with the given entries.
923      * @param {Array} engines List of available search engines.
924      * @param {number} defaultValue The value of the current default engine.
925      * @param {boolean} defaultManaged Whether the default search provider is
926      *     managed. If true, the default search provider can't be changed.
927      * @private
928      */
929     updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
930       this.clearSearchEngines_();
931       engineSelect = $('default-search-engine');
932       engineSelect.disabled = defaultManaged;
933       if (defaultManaged && defaultValue == -1)
934         return;
935       engineCount = engines.length;
936       var defaultIndex = -1;
937       for (var i = 0; i < engineCount; i++) {
938         var engine = engines[i];
939         var option = new Option(engine.name, engine.index);
940         if (defaultValue == option.value)
941           defaultIndex = i;
942         engineSelect.appendChild(option);
943       }
944       if (defaultIndex >= 0)
945         engineSelect.selectedIndex = defaultIndex;
946     },
948     /**
949      * Set the default search engine based on the popup selection.
950      * @private
951      */
952     setDefaultSearchEngine_: function() {
953       var engineSelect = $('default-search-engine');
954       var selectedIndex = engineSelect.selectedIndex;
955       if (selectedIndex >= 0) {
956         var selection = engineSelect.options[selectedIndex];
957         chrome.send('setDefaultSearchEngine', [String(selection.value)]);
958       }
959     },
961    /**
962      * Sets or clear whether Chrome should Auto-launch on computer startup.
963      * @private
964      */
965     handleAutoLaunchChanged_: function() {
966       chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
967     },
969     /**
970      * Get the selected profile item from the profile list. This also works
971      * correctly if the list is not displayed.
972      * @return {Object} the profile item object, or null if nothing is selected.
973      * @private
974      */
975     getSelectedProfileItem_: function() {
976       var profilesList = $('profiles-list');
977       if (profilesList.hidden) {
978         if (profilesList.dataModel.length > 0)
979           return profilesList.dataModel.item(0);
980       } else {
981         return profilesList.selectedItem;
982       }
983       return null;
984     },
986     /**
987      * Helper function to set the status of profile view buttons to disabled or
988      * enabled, depending on the number of profiles and selection status of the
989      * profiles list.
990      * @private
991      */
992     setProfileViewButtonsStatus_: function() {
993       var profilesList = $('profiles-list');
994       var selectedProfile = profilesList.selectedItem;
995       var hasSelection = selectedProfile != null;
996       var hasSingleProfile = profilesList.dataModel.length == 1;
997       var isManaged = loadTimeData.getBoolean('profileIsManaged');
998       $('profiles-manage').disabled = !hasSelection ||
999           !selectedProfile.isCurrentProfile;
1000       if (hasSelection && !selectedProfile.isCurrentProfile)
1001         $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1002       else
1003         $('profiles-manage').title = '';
1004       $('profiles-delete').disabled = isManaged ||
1005                                       (!hasSelection && !hasSingleProfile);
1006       if (OptionsPage.isSettingsApp()) {
1007         $('profiles-app-list-switch').disabled = !hasSelection ||
1008             selectedProfile.isCurrentProfile;
1009       }
1010       var importData = $('import-data');
1011       if (importData) {
1012         importData.disabled = $('import-data').disabled = hasSelection &&
1013           !selectedProfile.isCurrentProfile;
1014       }
1015     },
1017     /**
1018      * Display the correct dialog layout, depending on how many profiles are
1019      * available.
1020      * @param {number} numProfiles The number of profiles to display.
1021      * @private
1022      */
1023     setProfileViewSingle_: function(numProfiles) {
1024       var hasSingleProfile = numProfiles == 1;
1025       $('profiles-list').hidden = hasSingleProfile;
1026       $('profiles-single-message').hidden = !hasSingleProfile;
1027       $('profiles-manage').hidden =
1028           hasSingleProfile || OptionsPage.isSettingsApp();
1029       $('profiles-delete').textContent = hasSingleProfile ?
1030           loadTimeData.getString('profilesDeleteSingle') :
1031           loadTimeData.getString('profilesDelete');
1032       if (OptionsPage.isSettingsApp())
1033         $('profiles-app-list-switch').hidden = hasSingleProfile;
1034     },
1036     /**
1037      * Adds all |profiles| to the list.
1038      * @param {Array.<Object>} profiles An array of profile info objects.
1039      *     each object is of the form:
1040      *       profileInfo = {
1041      *         name: "Profile Name",
1042      *         iconURL: "chrome://path/to/icon/image",
1043      *         filePath: "/path/to/profile/data/on/disk",
1044      *         isCurrentProfile: false
1045      *       };
1046      * @private
1047      */
1048     setProfilesInfo_: function(profiles) {
1049       this.setProfileViewSingle_(profiles.length);
1050       // add it to the list, even if the list is hidden so we can access it
1051       // later.
1052       $('profiles-list').dataModel = new ArrayDataModel(profiles);
1054       // Received new data. If showing the "manage" overlay, keep it up to
1055       // date. If showing the "delete" overlay, close it.
1056       if (ManageProfileOverlay.getInstance().visible &&
1057           !$('manage-profile-overlay-manage').hidden) {
1058         ManageProfileOverlay.showManageDialog();
1059       } else {
1060         ManageProfileOverlay.getInstance().visible = false;
1061       }
1063       this.setProfileViewButtonsStatus_();
1064     },
1066     /**
1067      * Reports a local error (e.g., disk full) to the "create" overlay during
1068      * profile creation.
1069      * @private
1070      */
1071     showCreateProfileLocalError_: function() {
1072       CreateProfileOverlay.onLocalError();
1073     },
1075     /**
1076     * Reports a remote error (e.g., a network error during managed-user
1077     * registration) to the "create" overlay during profile creation.
1078     * @private
1079     */
1080     showCreateProfileRemoteError_: function() {
1081       CreateProfileOverlay.onRemoteError();
1082     },
1084     /**
1085     * Reports successful profile creation to the "create" overlay.
1086      * @param {Object} profileInfo An object of the form:
1087      *     profileInfo = {
1088      *       name: "Profile Name",
1089      *       filePath: "/path/to/profile/data/on/disk"
1090      *       isManaged: (true|false),
1091      *     };
1092     * @private
1093     */
1094     showCreateProfileSuccess_: function(profileInfo) {
1095       CreateProfileOverlay.onSuccess(profileInfo);
1096     },
1098     /**
1099      * Returns the currently active profile for this browser window.
1100      * @return {Object} A profile info object.
1101      * @private
1102      */
1103     getCurrentProfile_: function() {
1104       for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1105         var profile = $('profiles-list').dataModel.item(i);
1106         if (profile.isCurrentProfile)
1107           return profile;
1108       }
1110       assert(false,
1111              'There should always be a current profile, but none found.');
1112     },
1114     setNativeThemeButtonEnabled_: function(enabled) {
1115       var button = $('themes-native-button');
1116       if (button)
1117         button.disabled = !enabled;
1118     },
1120     setThemesResetButtonEnabled_: function(enabled) {
1121       $('themes-reset').disabled = !enabled;
1122     },
1124     /**
1125      * (Re)loads IMG element with current user account picture.
1126      * @private
1127      */
1128     updateAccountPicture_: function() {
1129       var picture = $('account-picture');
1130       if (picture) {
1131         picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1132             Date.now();
1133       }
1134     },
1136     /**
1137      * Handle the 'add device' button click.
1138      * @private
1139      */
1140     handleAddBluetoothDevice_: function() {
1141       chrome.send('findBluetoothDevices');
1142       OptionsPage.showPageByName('bluetooth', false);
1143     },
1145     /**
1146      * Enables factory reset section.
1147      * @private
1148      */
1149     enableFactoryResetSection_: function() {
1150       $('factory-reset-section').hidden = false;
1151     },
1153     /**
1154      * Set the checked state of the metrics reporting checkbox.
1155      * @private
1156      */
1157     setMetricsReportingCheckboxState_: function(checked, disabled) {
1158       $('metricsReportingEnabled').checked = checked;
1159       $('metricsReportingEnabled').disabled = disabled;
1160     },
1162     /**
1163      * @private
1164      */
1165     setMetricsReportingSettingVisibility_: function(visible) {
1166       if (visible)
1167         $('metricsReportingSetting').style.display = 'block';
1168       else
1169         $('metricsReportingSetting').style.display = 'none';
1170     },
1172     /**
1173      * Set the visibility of the password generation checkbox.
1174      * @private
1175      */
1176     setPasswordGenerationSettingVisibility_: function(visible) {
1177       if (visible)
1178         $('password-generation-checkbox').style.display = 'block';
1179       else
1180         $('password-generation-checkbox').style.display = 'none';
1181     },
1183     /**
1184      * Set the font size selected item. This item actually reflects two
1185      * preferences: the default font size and the default fixed font size.
1186      *
1187      * @param {Object} pref Information about the font size preferences.
1188      * @param {number} pref.value The value of the default font size pref.
1189      * @param {boolean} pref.disabled True if either pref not user modifiable.
1190      * @param {string} pref.controlledBy The source of the pref value(s) if
1191      *     either pref is currently not controlled by the user.
1192      * @private
1193      */
1194     setFontSize_: function(pref) {
1195       var selectCtl = $('defaultFontSize');
1196       selectCtl.disabled = pref.disabled;
1197       // Create a synthetic pref change event decorated as
1198       // CoreOptionsHandler::CreateValueForPref() does.
1199       var event = new cr.Event('synthetic-font-size');
1200       event.value = {
1201         value: pref.value,
1202         controlledBy: pref.controlledBy,
1203         disabled: pref.disabled
1204       };
1205       $('font-size-indicator').handlePrefChange(event);
1207       for (var i = 0; i < selectCtl.options.length; i++) {
1208         if (selectCtl.options[i].value == pref.value) {
1209           selectCtl.selectedIndex = i;
1210           if ($('Custom'))
1211             selectCtl.remove($('Custom').index);
1212           return;
1213         }
1214       }
1216       // Add/Select Custom Option in the font size label list.
1217       if (!$('Custom')) {
1218         var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1219                                 -1, false, true);
1220         option.setAttribute('id', 'Custom');
1221         selectCtl.add(option);
1222       }
1223       $('Custom').selected = true;
1224     },
1226     /**
1227      * Populate the page zoom selector with values received from the caller.
1228      * @param {Array} items An array of items to populate the selector.
1229      *     each object is an array with three elements as follows:
1230      *       0: The title of the item (string).
1231      *       1: The value of the item (number).
1232      *       2: Whether the item should be selected (boolean).
1233      * @private
1234      */
1235     setupPageZoomSelector_: function(items) {
1236       var element = $('defaultZoomFactor');
1238       // Remove any existing content.
1239       element.textContent = '';
1241       // Insert new child nodes into select element.
1242       var value, title, selected;
1243       for (var i = 0; i < items.length; i++) {
1244         title = items[i][0];
1245         value = items[i][1];
1246         selected = items[i][2];
1247         element.appendChild(new Option(title, value, false, selected));
1248       }
1249     },
1251     /**
1252      * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1253      * animation.
1254      * @param {boolean} display Whether to show the button and label or not.
1255      * @private
1256      */
1257     setAutoOpenFileTypesDisplayed_: function(display) {
1258       if (cr.isChromeOS)
1259         return;
1261       if ($('advanced-settings').hidden) {
1262         // If the Advanced section is hidden, don't animate the transition.
1263         $('auto-open-file-types-section').hidden = !display;
1264       } else {
1265         if (display) {
1266           this.showSectionWithAnimation_(
1267               $('auto-open-file-types-section'),
1268               $('auto-open-file-types-container'));
1269         } else {
1270           this.hideSectionWithAnimation_(
1271               $('auto-open-file-types-section'),
1272               $('auto-open-file-types-container'));
1273         }
1274       }
1275     },
1277     /**
1278      * Set the enabled state for the proxy settings button.
1279      * @private
1280      */
1281     setupProxySettingsSection_: function(disabled, extensionControlled) {
1282       if (!cr.isChromeOS) {
1283         $('proxiesConfigureButton').disabled = disabled;
1284         $('proxiesLabel').textContent =
1285             loadTimeData.getString(extensionControlled ?
1286                 'proxiesLabelExtension' : 'proxiesLabelSystem');
1287       }
1288     },
1290     /**
1291      * Set the Cloud Print proxy UI to enabled, disabled, or processing.
1292      * @private
1293      */
1294     setupCloudPrintConnectorSection_: function(disabled, label, allowed) {
1295       if (!cr.isChromeOS) {
1296         $('cloudPrintConnectorLabel').textContent = label;
1297         if (disabled || !allowed) {
1298           $('cloudPrintConnectorSetupButton').textContent =
1299             loadTimeData.getString('cloudPrintConnectorDisabledButton');
1300           $('cloudPrintManageButton').style.display = 'none';
1301         } else {
1302           $('cloudPrintConnectorSetupButton').textContent =
1303             loadTimeData.getString('cloudPrintConnectorEnabledButton');
1304           $('cloudPrintManageButton').style.display = 'inline';
1305         }
1306         $('cloudPrintConnectorSetupButton').disabled = !allowed;
1307       }
1308     },
1310     /**
1311      * @private
1312      */
1313     removeCloudPrintConnectorSection_: function() {
1314      if (!cr.isChromeOS) {
1315         var connectorSectionElm = $('cloud-print-connector-section');
1316         if (connectorSectionElm)
1317           connectorSectionElm.parentNode.removeChild(connectorSectionElm);
1318       }
1319     },
1321     /**
1322      * Set the initial state of the spoken feedback checkbox.
1323      * @private
1324      */
1325     setSpokenFeedbackCheckboxState_: function(checked) {
1326       $('accessibility-spoken-feedback-check').checked = checked;
1327     },
1329     /**
1330      * Set the initial state of the high contrast checkbox.
1331      * @private
1332      */
1333     setHighContrastCheckboxState_: function(checked) {
1334       $('accessibility-high-contrast-check').checked = checked;
1335     },
1337     /**
1338      * Set the initial state of the virtual keyboard checkbox.
1339      * @private
1340      */
1341     setVirtualKeyboardCheckboxState_: function(checked) {
1342       // TODO(zork): Update UI
1343     },
1345     /**
1346      * Show/hide mouse settings slider.
1347      * @private
1348      */
1349     showMouseControls_: function(show) {
1350       $('mouse-settings').hidden = !show;
1351     },
1353     /**
1354      * Show/hide touchpad-related settings.
1355      * @private
1356      */
1357     showTouchpadControls_: function(show) {
1358       $('touchpad-settings').hidden = !show;
1359       $('accessibility-tap-dragging').hidden = !show;
1360     },
1362     /**
1363      * Activate the Bluetooth settings section on the System settings page.
1364      * @private
1365      */
1366     showBluetoothSettings_: function() {
1367       $('bluetooth-devices').hidden = false;
1368     },
1370     /**
1371      * Dectivates the Bluetooth settings section from the System settings page.
1372      * @private
1373      */
1374     hideBluetoothSettings_: function() {
1375       $('bluetooth-devices').hidden = true;
1376     },
1378     /**
1379      * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1380      * state of the "Find devices" button and the list of discovered devices may
1381      * also be affected by a change to the state.
1382      * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1383      * @private
1384      */
1385     setBluetoothState_: function(checked) {
1386       $('enable-bluetooth').checked = checked;
1387       $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1388       $('bluetooth-add-device').hidden = !checked;
1389       $('bluetooth-reconnect-device').hidden = !checked;
1390       // Flush list of previously discovered devices if bluetooth is turned off.
1391       if (!checked) {
1392         $('bluetooth-paired-devices-list').clear();
1393         $('bluetooth-unpaired-devices-list').clear();
1394       } else {
1395         chrome.send('getPairedBluetoothDevices');
1396       }
1397     },
1399     /**
1400      * Adds an element to the list of available Bluetooth devices. If an element
1401      * with a matching address is found, the existing element is updated.
1402      * @param {{name: string,
1403      *          address: string,
1404      *          paired: boolean,
1405      *          connected: boolean}} device
1406      *     Decription of the Bluetooth device.
1407      * @private
1408      */
1409     addBluetoothDevice_: function(device) {
1410       var list = $('bluetooth-unpaired-devices-list');
1411       // Display the "connecting" (already paired or not yet paired) and the
1412       // paired devices in the same list.
1413       if (device.paired || device.connecting) {
1414         // Test to see if the device is currently in the unpaired list, in which
1415         // case it should be removed from that list.
1416         var index = $('bluetooth-unpaired-devices-list').find(device.address);
1417         if (index != undefined)
1418           $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1419         list = $('bluetooth-paired-devices-list');
1420       } else {
1421         // Test to see if the device is currently in the paired list, in which
1422         // case it should be removed from that list.
1423         var index = $('bluetooth-paired-devices-list').find(device.address);
1424         if (index != undefined)
1425           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1426       }
1427       list.appendDevice(device);
1429       // One device can be in the process of pairing.  If found, display
1430       // the Bluetooth pairing overlay.
1431       if (device.pairing)
1432         BluetoothPairing.showDialog(device);
1433     },
1435     /**
1436      * Removes an element from the list of available devices.
1437      * @param {string} address Unique address of the device.
1438      * @private
1439      */
1440     removeBluetoothDevice_: function(address) {
1441       var index = $('bluetooth-unpaired-devices-list').find(address);
1442       if (index != undefined) {
1443         $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1444       } else {
1445         index = $('bluetooth-paired-devices-list').find(address);
1446         if (index != undefined)
1447           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1448       }
1449     }
1450   };
1452   //Forward public APIs to private implementations.
1453   [
1454     'addBluetoothDevice',
1455     'enableFactoryResetSection',
1456     'getCurrentProfile',
1457     'getStartStopSyncButton',
1458     'hideBluetoothSettings',
1459     'notifyInitializationComplete',
1460     'removeBluetoothDevice',
1461     'removeCloudPrintConnectorSection',
1462     'scrollToSection',
1463     'setAutoOpenFileTypesDisplayed',
1464     'setBluetoothState',
1465     'setFontSize',
1466     'setNativeThemeButtonEnabled',
1467     'setHighContrastCheckboxState',
1468     'setMetricsReportingCheckboxState',
1469     'setMetricsReportingSettingVisibility',
1470     'setPasswordGenerationSettingVisibility',
1471     'setProfilesInfo',
1472     'setSpokenFeedbackCheckboxState',
1473     'setThemesResetButtonEnabled',
1474     'setVirtualKeyboardCheckboxState',
1475     'setupCloudPrintConnectorSection',
1476     'setupPageZoomSelector',
1477     'setupProxySettingsSection',
1478     'showBluetoothSettings',
1479     'showCreateProfileLocalError',
1480     'showCreateProfileRemoteError',
1481     'showCreateProfileSuccess',
1482     'showMouseControls',
1483     'showTouchpadControls',
1484     'updateAccountPicture',
1485     'updateAutoLaunchState',
1486     'updateDefaultBrowserState',
1487     'updateSearchEngines',
1488     'updateStartupPages',
1489     'updateSyncState',
1490   ].forEach(function(name) {
1491     BrowserOptions[name] = function() {
1492       var instance = BrowserOptions.getInstance();
1493       return instance[name + '_'].apply(instance, arguments);
1494     };
1495   });
1497   if (cr.isChromeOS) {
1498     /**
1499      * Returns username (canonical email) of the user logged in (ChromeOS only).
1500      * @return {string} user email.
1501      */
1502     // TODO(jhawkins): Investigate the use case for this method.
1503     BrowserOptions.getLoggedInUsername = function() {
1504       return BrowserOptions.getInstance().username_;
1505     };
1506   }
1508   // Export
1509   return {
1510     BrowserOptions: BrowserOptions
1511   };