Home | History | Annotate | Download | only in options
      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', function() {
      6   var Page = cr.ui.pageManager.Page;
      7   var PageManager = cr.ui.pageManager.PageManager;
      8   var ArrayDataModel = cr.ui.ArrayDataModel;
      9 
     10   /**
     11    * ManageProfileOverlay class
     12    * Encapsulated handling of the 'Manage profile...' overlay page.
     13    * @constructor
     14    * @extends {cr.ui.pageManager.Page}
     15    */
     16   function ManageProfileOverlay() {
     17     Page.call(this, 'manageProfile',
     18               loadTimeData.getString('manageProfileTabTitle'),
     19               'manage-profile-overlay');
     20   };
     21 
     22   cr.addSingletonGetter(ManageProfileOverlay);
     23 
     24   ManageProfileOverlay.prototype = {
     25     // Inherit from Page.
     26     __proto__: Page.prototype,
     27 
     28     // Info about the currently managed/deleted profile.
     29     profileInfo_: null,
     30 
     31     // Whether the currently chosen name for a new profile was assigned
     32     // automatically by choosing an avatar. Set on receiveNewProfileDefaults;
     33     // cleared on first edit (in onNameChanged_).
     34     profileNameIsDefault_: false,
     35 
     36     // List of default profile names corresponding to the respective icons.
     37     defaultProfileNames_: [],
     38 
     39     // An object containing all names of existing profiles.
     40     existingProfileNames_: {},
     41 
     42     // The currently selected icon in the icon grid.
     43     iconGridSelectedURL_: null,
     44 
     45     /** @override */
     46     initializePage: function() {
     47       Page.prototype.initializePage.call(this);
     48 
     49       var self = this;
     50       options.ProfilesIconGrid.decorate($('manage-profile-icon-grid'));
     51       options.ProfilesIconGrid.decorate($('create-profile-icon-grid'));
     52       self.registerCommonEventHandlers_('create',
     53                                         self.submitCreateProfile_.bind(self));
     54       self.registerCommonEventHandlers_('manage',
     55                                         self.submitManageChanges_.bind(self));
     56 
     57       // Override the create-profile-ok and create-* keydown handlers, to avoid
     58       // closing the overlay until we finish creating the profile.
     59       $('create-profile-ok').onclick = function(event) {
     60         self.submitCreateProfile_();
     61       };
     62 
     63       $('create-profile-cancel').onclick = function(event) {
     64         CreateProfileOverlay.cancelCreateProfile();
     65       };
     66 
     67       $('manage-profile-cancel').onclick =
     68           $('disconnect-managed-profile-cancel').onclick =
     69           $('delete-profile-cancel').onclick = function(event) {
     70         PageManager.closeOverlay();
     71       };
     72       $('delete-profile-ok').onclick = function(event) {
     73         PageManager.closeOverlay();
     74         if (BrowserOptions.getCurrentProfile().isSupervised)
     75           return;
     76         chrome.send('deleteProfile', [self.profileInfo_.filePath]);
     77         options.SupervisedUserListData.resetPromise();
     78       };
     79       $('add-shortcut-button').onclick = function(event) {
     80         chrome.send('addProfileShortcut', [self.profileInfo_.filePath]);
     81       };
     82       $('remove-shortcut-button').onclick = function(event) {
     83         chrome.send('removeProfileShortcut', [self.profileInfo_.filePath]);
     84       };
     85 
     86       $('disconnect-managed-profile-ok').onclick = function(event) {
     87         PageManager.closeOverlay();
     88         chrome.send('deleteProfile',
     89                     [BrowserOptions.getCurrentProfile().filePath]);
     90       };
     91 
     92       $('create-profile-supervised-signed-in-learn-more-link').onclick =
     93           function(event) {
     94         PageManager.showPageByName('supervisedUserLearnMore');
     95         return false;
     96       };
     97 
     98       $('create-profile-supervised-sign-in-link').onclick =
     99           function(event) {
    100         // The signin process will open an overlay to configure sync, which
    101         // would replace this overlay. It's smoother to close this one now.
    102         // TODO(pamg): Move the sync-setup overlay to a higher layer so this one
    103         // can stay open under it, after making sure that doesn't break anything
    104         // else.
    105         PageManager.closeOverlay();
    106         SyncSetupOverlay.startSignIn();
    107       };
    108 
    109       $('create-profile-supervised-sign-in-again-link').onclick =
    110           function(event) {
    111         PageManager.closeOverlay();
    112         SyncSetupOverlay.showSetupUI();
    113       };
    114 
    115       $('import-existing-supervised-user-link').onclick = function(event) {
    116         // Hide the import button to trigger a cursor update. The import button
    117         // is shown again when the import overlay loads. TODO(akuegel): Remove
    118         // this temporary fix when crbug/246304 is resolved.
    119         $('import-existing-supervised-user-link').hidden = true;
    120         PageManager.showPageByName('supervisedUserImport');
    121       };
    122     },
    123 
    124     /** @override */
    125     didShowPage: function() {
    126       chrome.send('requestDefaultProfileIcons', ['manage']);
    127 
    128       // Just ignore the manage profile dialog on Chrome OS, they use /accounts.
    129       if (!cr.isChromeOS && window.location.pathname == '/manageProfile')
    130         ManageProfileOverlay.getInstance().prepareForManageDialog_();
    131 
    132       // When editing a profile, initially hide the "add shortcut" and
    133       // "remove shortcut" buttons and ask the handler which to show. It will
    134       // call |receiveHasProfileShortcuts|, which will show the appropriate one.
    135       $('remove-shortcut-button').hidden = true;
    136       $('add-shortcut-button').hidden = true;
    137 
    138       if (loadTimeData.getBoolean('profileShortcutsEnabled')) {
    139         var profileInfo = ManageProfileOverlay.getInstance().profileInfo_;
    140         chrome.send('requestHasProfileShortcuts', [profileInfo.filePath]);
    141       }
    142 
    143       var manageNameField = $('manage-profile-name');
    144       // Supervised users cannot edit their names.
    145       if (manageNameField.disabled)
    146         $('manage-profile-ok').focus();
    147       else
    148         manageNameField.focus();
    149 
    150       this.profileNameIsDefault_ = false;
    151     },
    152 
    153     /**
    154      * Registers event handlers that are common between create and manage modes.
    155      * @param {string} mode A label that specifies the type of dialog box which
    156      *     is currently being viewed (i.e. 'create' or 'manage').
    157      * @param {function()} submitFunction The function that should be called
    158      *     when the user chooses to submit (e.g. by clicking the OK button).
    159      * @private
    160      */
    161     registerCommonEventHandlers_: function(mode, submitFunction) {
    162       var self = this;
    163       $(mode + '-profile-icon-grid').addEventListener('change', function(e) {
    164         self.onIconGridSelectionChanged_(mode);
    165       });
    166       $(mode + '-profile-name').oninput = function(event) {
    167         self.onNameChanged_(mode);
    168       };
    169       $(mode + '-profile-ok').onclick = function(event) {
    170         PageManager.closeOverlay();
    171         submitFunction();
    172       };
    173     },
    174 
    175     /**
    176      * Set the profile info used in the dialog.
    177      * @param {Object} profileInfo An object of the form:
    178      *     profileInfo = {
    179      *       name: "Profile Name",
    180      *       iconURL: "chrome://path/to/icon/image",
    181      *       filePath: "/path/to/profile/data/on/disk",
    182      *       isCurrentProfile: false,
    183      *       isSupervised: false
    184      *     };
    185      * @param {string} mode A label that specifies the type of dialog box which
    186      *     is currently being viewed (i.e. 'create' or 'manage').
    187      * @private
    188      */
    189     setProfileInfo_: function(profileInfo, mode) {
    190       this.iconGridSelectedURL_ = profileInfo.iconURL;
    191       this.profileInfo_ = profileInfo;
    192       $(mode + '-profile-name').value = profileInfo.name;
    193       $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL;
    194     },
    195 
    196     /**
    197      * Sets the name of the profile being edited or created.
    198      * @param {string} name New profile name.
    199      * @param {string} mode A label that specifies the type of dialog box which
    200      *     is currently being viewed (i.e. 'create' or 'manage').
    201      * @private
    202      */
    203     setProfileName_: function(name, mode) {
    204       if (this.profileInfo_)
    205         this.profileInfo_.name = name;
    206       $(mode + '-profile-name').value = name;
    207     },
    208 
    209     /**
    210      * Set an array of default icon URLs. These will be added to the grid that
    211      * the user will use to choose their profile icon.
    212      * @param {string} mode A label that specifies the type of dialog box which
    213      *     is currently being viewed (i.e. 'create' or 'manage').
    214      * @param {!Array.<string>} iconURLs An array of icon URLs.
    215      * @param {Array.<string>} names An array of default names
    216      *     corresponding to the icons.
    217      * @private
    218      */
    219     receiveDefaultProfileIconsAndNames_: function(mode, iconURLs, names) {
    220       this.defaultProfileNames_ = names;
    221 
    222       var grid = $(mode + '-profile-icon-grid');
    223 
    224       grid.dataModel = new ArrayDataModel(iconURLs);
    225 
    226       if (this.profileInfo_)
    227         grid.selectedItem = this.profileInfo_.iconURL;
    228 
    229       // Recalculate the measured item size.
    230       grid.measured_ = null;
    231       grid.columns = 0;
    232       grid.redraw();
    233     },
    234 
    235     /**
    236      * Callback to set the initial values when creating a new profile.
    237      * @param {Object} profileInfo An object of the form:
    238      *     profileInfo = {
    239      *       name: "Profile Name",
    240      *       iconURL: "chrome://path/to/icon/image",
    241      *     };
    242      * @private
    243      */
    244     receiveNewProfileDefaults_: function(profileInfo) {
    245       ManageProfileOverlay.setProfileInfo(profileInfo, 'create');
    246       this.profileNameIsDefault_ = true;
    247       $('create-profile-name-label').hidden = false;
    248       $('create-profile-name').hidden = false;
    249       // Trying to change the focus if this isn't the topmost overlay can
    250       // instead cause the FocusManager to override another overlay's focus,
    251       // e.g. if an overlay above this one is in the process of being reloaded.
    252       // But the C++ handler calls this method directly on ManageProfileOverlay,
    253       // so check the pageDiv to also include its subclasses (in particular
    254       // CreateProfileOverlay, which has higher sub-overlays).
    255       if (PageManager.getTopmostVisiblePage().pageDiv == this.pageDiv) {
    256         // This will only have an effect if the 'create-profile-name' element
    257         //  is visible, i.e. if the overlay is in create mode.
    258         $('create-profile-name').focus();
    259       }
    260       $('create-profile-ok').disabled = false;
    261     },
    262 
    263     /**
    264      * Set a dictionary of all profile names. These are used to prevent the
    265      * user from naming two profiles the same.
    266      * @param {Object} profileNames A dictionary of profile names.
    267      * @private
    268      */
    269     receiveExistingProfileNames_: function(profileNames) {
    270       this.existingProfileNames_ = profileNames;
    271     },
    272 
    273     /**
    274      * Callback to show the add/remove shortcut buttons when in edit mode,
    275      * called by the handler as a result of the 'requestHasProfileShortcuts_'
    276      * message.
    277      * @param {boolean} hasShortcuts Whether profile has any existing shortcuts.
    278      * @private
    279      */
    280     receiveHasProfileShortcuts_: function(hasShortcuts) {
    281       $('add-shortcut-button').hidden = hasShortcuts;
    282       $('remove-shortcut-button').hidden = !hasShortcuts;
    283     },
    284 
    285     /**
    286      * Display the error bubble, with |errorHtml| in the bubble.
    287      * @param {string} errorHtml The html string to display as an error.
    288      * @param {string} mode A label that specifies the type of dialog box which
    289      *     is currently being viewed (i.e. 'create' or 'manage').
    290      * @param {boolean} disableOKButton True if the dialog's OK button should be
    291      *     disabled when the error bubble is shown. It will be (re-)enabled when
    292      *     the error bubble is hidden.
    293      * @private
    294      */
    295     showErrorBubble_: function(errorHtml, mode, disableOKButton) {
    296       var nameErrorEl = $(mode + '-profile-error-bubble');
    297       nameErrorEl.hidden = false;
    298       nameErrorEl.innerHTML = errorHtml;
    299 
    300       if (disableOKButton)
    301         $(mode + '-profile-ok').disabled = true;
    302     },
    303 
    304     /**
    305      * Hide the error bubble.
    306      * @param {string} mode A label that specifies the type of dialog box which
    307      *     is currently being viewed (i.e. 'create' or 'manage').
    308      * @private
    309      */
    310     hideErrorBubble_: function(mode) {
    311       $(mode + '-profile-error-bubble').innerHTML = '';
    312       $(mode + '-profile-error-bubble').hidden = true;
    313       $(mode + '-profile-ok').disabled = false;
    314     },
    315 
    316     /**
    317      * oninput callback for <input> field.
    318      * @param {string} mode A label that specifies the type of dialog box which
    319      *     is currently being viewed (i.e. 'create' or 'manage').
    320      * @private
    321      */
    322     onNameChanged_: function(mode) {
    323       this.profileNameIsDefault_ = false;
    324       this.updateCreateOrImport_(mode);
    325     },
    326 
    327     /**
    328      * Called when the profile name is changed or the 'create supervised'
    329      * checkbox is toggled. Updates the 'ok' button and the 'import existing
    330      * supervised user' link.
    331      * @param {string} mode A label that specifies the type of dialog box which
    332      *     is currently being viewed (i.e. 'create' or 'manage').
    333      * @private
    334      */
    335     updateCreateOrImport_: function(mode) {
    336       this.updateOkButton_(mode);
    337       // In 'create' mode, check for existing supervised users with the same
    338       // name.
    339       if (mode == 'create')
    340         this.requestExistingSupervisedUsers_();
    341     },
    342 
    343     /**
    344      * Tries to get the list of existing supervised users and updates the UI
    345      * accordingly.
    346      * @private
    347      */
    348     requestExistingSupervisedUsers_: function() {
    349       options.SupervisedUserListData.requestExistingSupervisedUsers().then(
    350           this.receiveExistingSupervisedUsers_.bind(this),
    351           this.onSigninError_.bind(this));
    352     },
    353 
    354     /**
    355      * @param {Object} supervisedUser
    356      * @param {boolean} nameIsUnique
    357      */
    358     getImportHandler_: function(supervisedUser, nameIsUnique) {
    359       return function() {
    360         if (supervisedUser.needAvatar || !nameIsUnique) {
    361           PageManager.showPageByName('supervisedUserImport');
    362         } else {
    363           this.hideErrorBubble_('create');
    364           CreateProfileOverlay.updateCreateInProgress(true);
    365           chrome.send('createProfile',
    366               [supervisedUser.name, supervisedUser.iconURL, false, true,
    367                    supervisedUser.id]);
    368         }
    369       }.bind(this);
    370     },
    371 
    372     /**
    373      * Callback which receives the list of existing supervised users. Checks if
    374      * the currently entered name is the name of an already existing supervised
    375      * user. If yes, the user is prompted to import the existing supervised
    376      * user, and the create button is disabled.
    377      * If the received list is empty, hides the "import" link.
    378      * @param {Array.<Object>} supervisedUsers The list of existing supervised
    379      *     users.
    380      * @private
    381      */
    382     receiveExistingSupervisedUsers_: function(supervisedUsers) {
    383       $('import-existing-supervised-user-link').hidden =
    384           supervisedUsers.length === 0;
    385       if (!$('create-profile-supervised').checked)
    386         return;
    387 
    388       var newName = $('create-profile-name').value;
    389       var i;
    390       for (i = 0; i < supervisedUsers.length; ++i) {
    391         if (supervisedUsers[i].name == newName &&
    392             !supervisedUsers[i].onCurrentDevice) {
    393           var errorHtml = loadTimeData.getStringF(
    394               'manageProfilesExistingSupervisedUser',
    395               HTMLEscape(elide(newName, /* maxLength */ 50)));
    396           this.showErrorBubble_(errorHtml, 'create', true);
    397 
    398           // Check if another supervised user also exists with that name.
    399           var nameIsUnique = true;
    400           var j;
    401           for (j = i + 1; j < supervisedUsers.length; ++j) {
    402             if (supervisedUsers[j].name == newName) {
    403               nameIsUnique = false;
    404               break;
    405             }
    406           }
    407           $('supervised-user-import-existing').onclick =
    408               this.getImportHandler_(supervisedUsers[i], nameIsUnique);
    409           $('create-profile-ok').disabled = true;
    410           return;
    411         }
    412       }
    413     },
    414 
    415     /**
    416      * Called in case the request for the list of supervised users fails because
    417      * of a signin error.
    418      * @private
    419      */
    420     onSigninError_: function() {
    421       this.updateSignedInStatus_(this.signedInEmail_, true);
    422     },
    423 
    424     /**
    425      * Called to update the state of the ok button depending if the name is
    426      * already used or not.
    427      * @param {string} mode A label that specifies the type of dialog box which
    428      *     is currently being viewed (i.e. 'create' or 'manage').
    429      * @private
    430      */
    431     updateOkButton_: function(mode) {
    432       var oldName = this.profileInfo_.name;
    433       var newName = $(mode + '-profile-name').value;
    434       var nameIsDuplicate = this.existingProfileNames_[newName] != undefined;
    435       if (mode == 'manage' && oldName == newName)
    436         nameIsDuplicate = false;
    437       if (nameIsDuplicate) {
    438         var errorHtml =
    439             loadTimeData.getString('manageProfilesDuplicateNameError');
    440         this.showErrorBubble_(errorHtml, mode, true);
    441       } else {
    442         this.hideErrorBubble_(mode);
    443 
    444         var nameIsValid = $(mode + '-profile-name').validity.valid;
    445         $(mode + '-profile-ok').disabled = !nameIsValid;
    446       }
    447     },
    448 
    449     /**
    450      * Called when the user clicks "OK" or hits enter. Saves the newly changed
    451      * profile info.
    452      * @private
    453      */
    454     submitManageChanges_: function() {
    455       var name = $('manage-profile-name').value;
    456       var iconURL = $('manage-profile-icon-grid').selectedItem;
    457 
    458       chrome.send('setProfileIconAndName',
    459                   [this.profileInfo_.filePath, iconURL, name]);
    460       if (name != this.profileInfo_.name)
    461         options.SupervisedUserListData.resetPromise();
    462     },
    463 
    464     /** @private */
    465     updateSignedInStatus_: assertNotReached,
    466 
    467     /**
    468      * Called when the user clicks "OK" or hits enter. Creates the profile
    469      * using the information in the dialog.
    470      * @private
    471      */
    472     submitCreateProfile_: function() {
    473       // This is visual polish: the UI to access this should be disabled for
    474       // supervised users, and the back end will prevent user creation anyway.
    475       if (this.profileInfo_ && this.profileInfo_.isSupervised)
    476         return;
    477 
    478       this.hideErrorBubble_('create');
    479       CreateProfileOverlay.updateCreateInProgress(true);
    480 
    481       // Get the user's chosen name and icon, or default if they do not
    482       // wish to customize their profile.
    483       var name = $('create-profile-name').value;
    484       var iconUrl = $('create-profile-icon-grid').selectedItem;
    485       var createShortcut = $('create-shortcut').checked;
    486       var isSupervised = $('create-profile-supervised').checked;
    487       var existingSupervisedUserId = '';
    488 
    489       // 'createProfile' is handled by the CreateProfileHandler.
    490       chrome.send('createProfile',
    491                   [name, iconUrl, createShortcut,
    492                    isSupervised, existingSupervisedUserId]);
    493     },
    494 
    495     /**
    496      * Called when the selected icon in the icon grid changes.
    497      * @param {string} mode A label that specifies the type of dialog box which
    498      *     is currently being viewed (i.e. 'create' or 'manage').
    499      * @private
    500      */
    501     onIconGridSelectionChanged_: function(mode) {
    502       var iconURL = $(mode + '-profile-icon-grid').selectedItem;
    503       if (!iconURL || iconURL == this.iconGridSelectedURL_)
    504         return;
    505       this.iconGridSelectedURL_ = iconURL;
    506       if (this.profileNameIsDefault_) {
    507         var index = $(mode + '-profile-icon-grid').selectionModel.selectedIndex;
    508         var name = this.defaultProfileNames_[index];
    509         if (name) {
    510           this.setProfileName_(name, mode);
    511           this.updateCreateOrImport_(mode);
    512         }
    513       }
    514       if (this.profileInfo_ && this.profileInfo_.filePath) {
    515         chrome.send('profileIconSelectionChanged',
    516                     [this.profileInfo_.filePath, iconURL]);
    517       }
    518     },
    519 
    520     /**
    521      * Updates the contents of the "Manage Profile" section of the dialog,
    522      * and shows that section.
    523      * @private
    524      */
    525     prepareForManageDialog_: function() {
    526       chrome.send('refreshGaiaPicture');
    527       var profileInfo = BrowserOptions.getCurrentProfile();
    528       ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
    529       $('manage-profile-overlay-create').hidden = true;
    530       $('manage-profile-overlay-manage').hidden = false;
    531       $('manage-profile-overlay-delete').hidden = true;
    532       $('manage-profile-overlay-disconnect-managed').hidden = true;
    533       $('manage-profile-name').disabled = profileInfo.isSupervised;
    534       this.hideErrorBubble_('manage');
    535     },
    536 
    537     /**
    538      * Display the "Manage Profile" dialog.
    539      * @private
    540      */
    541     showManageDialog_: function() {
    542       this.prepareForManageDialog_();
    543       PageManager.showPageByName('manageProfile');
    544     },
    545 
    546     /**
    547      * Display the "Delete Profile" dialog.
    548      * @param {Object} profileInfo The profile object of the profile to delete.
    549      * @private
    550      */
    551     showDeleteDialog_: function(profileInfo) {
    552       if (BrowserOptions.getCurrentProfile().isSupervised)
    553         return;
    554 
    555       ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
    556       $('manage-profile-overlay-create').hidden = true;
    557       $('manage-profile-overlay-manage').hidden = true;
    558       $('manage-profile-overlay-delete').hidden = false;
    559       $('manage-profile-overlay-disconnect-managed').hidden = true;
    560       $('delete-profile-icon').style.content =
    561           getProfileAvatarIcon(profileInfo.iconURL);
    562       $('delete-profile-text').textContent =
    563           loadTimeData.getStringF('deleteProfileMessage',
    564                                   elide(profileInfo.name, /* maxLength */ 50));
    565       $('delete-supervised-profile-addendum').hidden =
    566           !profileInfo.isSupervised;
    567 
    568       // Because this dialog isn't useful when refreshing or as part of the
    569       // history, don't create a history entry for it when showing.
    570       PageManager.showPageByName('manageProfile', false);
    571     },
    572 
    573     /**
    574      * Display the "Disconnect Managed Profile" dialog.
    575      * @private
    576      */
    577     showDisconnectManagedProfileDialog_: function(replacements) {
    578       loadTimeData.overrideValues(replacements);
    579       $('manage-profile-overlay-create').hidden = true;
    580       $('manage-profile-overlay-manage').hidden = true;
    581       $('manage-profile-overlay-delete').hidden = true;
    582       $('disconnect-managed-profile-domain-information').innerHTML =
    583           loadTimeData.getString('disconnectManagedProfileDomainInformation');
    584       $('disconnect-managed-profile-text').innerHTML =
    585           loadTimeData.getString('disconnectManagedProfileText');
    586       $('manage-profile-overlay-disconnect-managed').hidden = false;
    587 
    588       // Because this dialog isn't useful when refreshing or as part of the
    589       // history, don't create a history entry for it when showing.
    590       PageManager.showPageByName('manageProfile', false);
    591     },
    592 
    593     /**
    594      * Display the "Create Profile" dialog.
    595      * @private
    596      */
    597     showCreateDialog_: function() {
    598       PageManager.showPageByName('createProfile');
    599     },
    600   };
    601 
    602   // Forward public APIs to private implementations.
    603   cr.makePublic(ManageProfileOverlay, [
    604     'receiveDefaultProfileIconsAndNames',
    605     'receiveNewProfileDefaults',
    606     'receiveExistingProfileNames',
    607     'receiveHasProfileShortcuts',
    608     'setProfileInfo',
    609     'setProfileName',
    610     'showManageDialog',
    611     'showDeleteDialog',
    612     'showDisconnectManagedProfileDialog',
    613     'showCreateDialog',
    614   ]);
    615 
    616   function CreateProfileOverlay() {
    617     Page.call(this, 'createProfile',
    618               loadTimeData.getString('createProfileTabTitle'),
    619               'manage-profile-overlay');
    620   };
    621 
    622   cr.addSingletonGetter(CreateProfileOverlay);
    623 
    624   CreateProfileOverlay.prototype = {
    625     // Inherit from ManageProfileOverlay.
    626     __proto__: ManageProfileOverlay.prototype,
    627 
    628     // The signed-in email address of the current profile, or empty if they're
    629     // not signed in.
    630     signedInEmail_: '',
    631 
    632     /** @override */
    633     canShowPage: function() {
    634       return !BrowserOptions.getCurrentProfile().isSupervised;
    635     },
    636 
    637     /**
    638      * Configures the overlay to the "create user" mode.
    639      * @override
    640      */
    641     didShowPage: function() {
    642       chrome.send('requestCreateProfileUpdate');
    643       chrome.send('requestDefaultProfileIcons', ['create']);
    644       chrome.send('requestNewProfileDefaults');
    645 
    646       $('manage-profile-overlay-create').hidden = false;
    647       $('manage-profile-overlay-manage').hidden = true;
    648       $('manage-profile-overlay-delete').hidden = true;
    649       $('manage-profile-overlay-disconnect-managed').hidden = true;
    650       $('create-profile-instructions').textContent =
    651          loadTimeData.getStringF('createProfileInstructions');
    652       this.hideErrorBubble_();
    653       this.updateCreateInProgress_(false);
    654 
    655       var shortcutsEnabled = loadTimeData.getBoolean('profileShortcutsEnabled');
    656       $('create-shortcut-container').hidden = !shortcutsEnabled;
    657       $('create-shortcut').checked = shortcutsEnabled;
    658 
    659       $('create-profile-name-label').hidden = true;
    660       $('create-profile-name').hidden = true;
    661       $('create-profile-ok').disabled = true;
    662 
    663       $('create-profile-supervised').checked = false;
    664       $('import-existing-supervised-user-link').hidden = true;
    665       $('create-profile-supervised').onchange = function() {
    666         ManageProfileOverlay.getInstance().updateCreateOrImport_('create');
    667       };
    668       $('create-profile-supervised').hidden = true;
    669       $('create-profile-supervised-signed-in').disabled = true;
    670       $('create-profile-supervised-signed-in').hidden = true;
    671       $('create-profile-supervised-not-signed-in').hidden = true;
    672 
    673       this.profileNameIsDefault_ = false;
    674     },
    675 
    676     /** @override */
    677     handleCancel: function() {
    678       this.cancelCreateProfile_();
    679     },
    680 
    681     /** @override */
    682     showErrorBubble_: function(errorHtml) {
    683       ManageProfileOverlay.getInstance().showErrorBubble_(errorHtml,
    684                                                           'create',
    685                                                           false);
    686     },
    687 
    688     /** @override */
    689     hideErrorBubble_: function() {
    690       ManageProfileOverlay.getInstance().hideErrorBubble_('create');
    691     },
    692 
    693     /**
    694      * Updates the UI when a profile create step begins or ends.
    695      * Note that hideErrorBubble_() also enables the "OK" button, so it
    696      * must be called before this function if both are used.
    697      * @param {boolean} inProgress True if the UI should be updated to show that
    698      *     profile creation is now in progress.
    699      * @private
    700      */
    701     updateCreateInProgress_: function(inProgress) {
    702       this.createInProgress_ = inProgress;
    703       this.updateCreateSupervisedUserCheckbox_();
    704 
    705       $('create-profile-icon-grid').disabled = inProgress;
    706       $('create-profile-name').disabled = inProgress;
    707       $('create-shortcut').disabled = inProgress;
    708       $('create-profile-ok').disabled = inProgress;
    709       $('import-existing-supervised-user-link').disabled = inProgress;
    710 
    711       $('create-profile-throbber').hidden = !inProgress;
    712     },
    713 
    714     /**
    715      * Cancels the creation of the a profile. It is safe to call this even
    716      * when no profile is in the process of being created.
    717      * @private
    718      */
    719     cancelCreateProfile_: function() {
    720       PageManager.closeOverlay();
    721       chrome.send('cancelCreateProfile');
    722       this.hideErrorBubble_();
    723       this.updateCreateInProgress_(false);
    724     },
    725 
    726     /**
    727      * Shows an error message describing an error that occurred while creating
    728      * a new profile.
    729      * Called by BrowserOptions via the BrowserOptionsHandler.
    730      * @param {string} error The error message to display.
    731      * @private
    732      */
    733     onError_: function(error) {
    734       this.updateCreateInProgress_(false);
    735       this.showErrorBubble_(error);
    736     },
    737 
    738     /**
    739      * Shows a warning message giving information while creating a new profile.
    740      * Called by BrowserOptions via the BrowserOptionsHandler.
    741      * @param {string} warning The warning message to display.
    742      * @private
    743      */
    744     onWarning_: function(warning) {
    745       this.showErrorBubble_(warning);
    746     },
    747 
    748     /**
    749      * For new supervised users, shows a confirmation page after successfully
    750      * creating a new profile; otherwise, the handler will open a new window.
    751      * @param {Object} profileInfo An object of the form:
    752      *     profileInfo = {
    753      *       name: "Profile Name",
    754      *       filePath: "/path/to/profile/data/on/disk"
    755      *       isSupervised: (true|false),
    756      *     };
    757      * @private
    758      */
    759     onSuccess_: function(profileInfo) {
    760       this.updateCreateInProgress_(false);
    761       PageManager.closeOverlay();
    762       if (profileInfo.isSupervised) {
    763         options.SupervisedUserListData.resetPromise();
    764         profileInfo.custodianEmail = this.signedInEmail_;
    765         SupervisedUserCreateConfirmOverlay.setProfileInfo(profileInfo);
    766         PageManager.showPageByName('supervisedUserCreateConfirm', false);
    767         BrowserOptions.updateManagesSupervisedUsers(true);
    768       }
    769     },
    770 
    771     /**
    772      * Updates the signed-in or not-signed-in UI when in create mode. Called by
    773      * the handler in response to the 'requestCreateProfileUpdate' message.
    774      * updateSupervisedUsersAllowed_ is expected to be called after this is, and
    775      * will update additional UI elements.
    776      * @param {string} email The email address of the currently signed-in user.
    777      *     An empty string indicates that the user is not signed in.
    778      * @param {boolean} hasError Whether the user's sign-in credentials are
    779      *     still valid.
    780      * @private
    781      */
    782     updateSignedInStatus_: function(email, hasError) {
    783       this.signedInEmail_ = email;
    784       this.hasError_ = hasError;
    785       var isSignedIn = email !== '';
    786       $('create-profile-supervised').hidden = !isSignedIn;
    787       $('create-profile-supervised-signed-in').hidden = !isSignedIn;
    788       $('create-profile-supervised-not-signed-in').hidden = isSignedIn;
    789 
    790       if (isSignedIn) {
    791         var accountDetailsOutOfDate =
    792             $('create-profile-supervised-account-details-out-of-date-label');
    793         accountDetailsOutOfDate.textContent = loadTimeData.getStringF(
    794             'manageProfilesSupervisedAccountDetailsOutOfDate', email);
    795         accountDetailsOutOfDate.hidden = !hasError;
    796 
    797         $('create-profile-supervised-signed-in-label').textContent =
    798             loadTimeData.getStringF(
    799                 'manageProfilesSupervisedSignedInLabel', email);
    800         $('create-profile-supervised-signed-in-label').hidden = hasError;
    801 
    802         $('create-profile-supervised-sign-in-again-link').hidden = !hasError;
    803         $('create-profile-supervised-signed-in-learn-more-link').hidden =
    804             hasError;
    805       }
    806 
    807       this.updateCreateSupervisedUserCheckbox_();
    808       // If we're signed in, showing/hiding import-existing-supervised-user-link
    809       // is handled in receiveExistingSupervisedUsers_.
    810       if (isSignedIn && !hasError)
    811         this.requestExistingSupervisedUsers_();
    812       else
    813         $('import-existing-supervised-user-link').hidden = true;
    814     },
    815 
    816     /**
    817      * Sets whether creating supervised users is allowed or not. Called by the
    818      * handler in response to the 'requestCreateProfileUpdate' message or a
    819      * change in the (policy-controlled) pref that prohibits creating supervised
    820      * users, after the signed-in status has been updated.
    821      * @param {boolean} allowed True if creating supervised users should be
    822      *     allowed.
    823      * @private
    824      */
    825     updateSupervisedUsersAllowed_: function(allowed) {
    826       this.supervisedUsersAllowed_ = allowed;
    827       this.updateCreateSupervisedUserCheckbox_();
    828 
    829       $('create-profile-supervised-sign-in-link').enabled = allowed;
    830       if (!allowed) {
    831         $('create-profile-supervised-indicator').setAttribute('controlled-by',
    832                                                               'policy');
    833       } else {
    834         $('create-profile-supervised-indicator').removeAttribute(
    835             'controlled-by');
    836       }
    837     },
    838 
    839     /**
    840      * Updates the status of the "create supervised user" checkbox. Called from
    841      * updateSupervisedUsersAllowed_() or updateCreateInProgress_().
    842      * updateSignedInStatus_() does not call this method directly, because it
    843      * will be followed by a call to updateSupervisedUsersAllowed_().
    844      * @private
    845      */
    846     updateCreateSupervisedUserCheckbox_: function() {
    847       $('create-profile-supervised').disabled =
    848           !this.supervisedUsersAllowed_ || this.createInProgress_ ||
    849           this.signedInEmail_ == '' || this.hasError_;
    850     },
    851   };
    852 
    853   // Forward public APIs to private implementations.
    854   cr.makePublic(CreateProfileOverlay, [
    855     'cancelCreateProfile',
    856     'onError',
    857     'onSuccess',
    858     'onWarning',
    859     'updateCreateInProgress',
    860     'updateSignedInStatus',
    861     'updateSupervisedUsersAllowed',
    862   ]);
    863 
    864   // Export
    865   return {
    866     ManageProfileOverlay: ManageProfileOverlay,
    867     CreateProfileOverlay: CreateProfileOverlay,
    868   };
    869 });
    870