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;
10 * ManageProfileOverlay class
11 * Encapsulated handling of the 'Manage profile...' overlay page.
15 function ManageProfileOverlay() {
16 OptionsPage.call(this, 'manageProfile',
17 loadTimeData.getString('manageProfileTabTitle'),
18 'manage-profile-overlay');
21 cr.addSingletonGetter(ManageProfileOverlay);
23 ManageProfileOverlay.prototype = {
24 // Inherit from OptionsPage.
25 __proto__: OptionsPage.prototype,
27 // Info about the currently managed/deleted profile.
30 // An object containing all known profile names.
33 // The currently selected icon in the icon grid.
34 iconGridSelectedURL_: null,
37 * Initialize the page.
39 initializePage: function() {
40 // Call base class implementation to start preference initialization.
41 OptionsPage.prototype.initializePage.call(this);
44 options.ProfilesIconGrid.decorate($('manage-profile-icon-grid'));
45 options.ProfilesIconGrid.decorate($('create-profile-icon-grid'));
46 self.registerCommonEventHandlers_('create',
47 self.submitCreateProfile_.bind(self));
48 self.registerCommonEventHandlers_('manage',
49 self.submitManageChanges_.bind(self));
51 // Override the create-profile-ok and create-* keydown handlers, to avoid
52 // closing the overlay until we finish creating the profile.
53 $('create-profile-ok').onclick = function(event) {
54 self.submitCreateProfile_();
57 $('create-profile-cancel').onclick = function(event) {
58 CreateProfileOverlay.cancelCreateProfile();
61 $('manage-profile-cancel').onclick =
62 $('delete-profile-cancel').onclick = function(event) {
63 OptionsPage.closeOverlay();
65 $('delete-profile-ok').onclick = function(event) {
66 OptionsPage.closeOverlay();
67 if (BrowserOptions.getCurrentProfile().isManaged)
69 chrome.send('deleteProfile', [self.profileInfo_.filePath]);
70 options.ManagedUserListData.resetPromise();
72 $('add-shortcut-button').onclick = function(event) {
73 chrome.send('addProfileShortcut', [self.profileInfo_.filePath]);
75 $('remove-shortcut-button').onclick = function(event) {
76 chrome.send('removeProfileShortcut', [self.profileInfo_.filePath]);
79 $('create-profile-managed-signed-in-learn-more-link').onclick =
81 OptionsPage.navigateToPage('managedUserLearnMore');
85 $('create-profile-managed-not-signed-in-link').onclick = function(event) {
86 // The signin process will open an overlay to configure sync, which
87 // would replace this overlay. It's smoother to close this one now.
88 // TODO(pamg): Move the sync-setup overlay to a higher layer so this one
89 // can stay open under it, after making sure that doesn't break anything
91 OptionsPage.closeOverlay();
92 SyncSetupOverlay.startSignIn();
95 $('create-profile-managed-sign-in-again-link').onclick = function(event) {
96 OptionsPage.closeOverlay();
97 SyncSetupOverlay.showSetupUI();
100 $('import-existing-managed-user-link').onclick = function(event) {
101 OptionsPage.navigateToPage('managedUserImport');
106 didShowPage: function() {
107 chrome.send('requestDefaultProfileIcons');
109 // Just ignore the manage profile dialog on Chrome OS, they use /accounts.
110 if (!cr.isChromeOS && window.location.pathname == '/manageProfile')
111 ManageProfileOverlay.getInstance().prepareForManageDialog_();
113 // When editing a profile, initially hide the "add shortcut" and
114 // "remove shortcut" buttons and ask the handler which to show. It will
115 // call |receiveHasProfileShortcuts|, which will show the appropriate one.
116 $('remove-shortcut-button').hidden = true;
117 $('add-shortcut-button').hidden = true;
119 if (loadTimeData.getBoolean('profileShortcutsEnabled')) {
120 var profileInfo = ManageProfileOverlay.getInstance().profileInfo_;
121 chrome.send('requestHasProfileShortcuts', [profileInfo.filePath]);
124 var manageNameField = $('manage-profile-name');
125 // Supervised users cannot edit their names.
126 if (manageNameField.disabled)
127 $('manage-profile-ok').focus();
129 manageNameField.focus();
133 * Registers event handlers that are common between create and manage modes.
134 * @param {string} mode A label that specifies the type of dialog box which
135 * is currently being viewed (i.e. 'create' or 'manage').
136 * @param {function()} submitFunction The function that should be called
137 * when the user chooses to submit (e.g. by clicking the OK button).
140 registerCommonEventHandlers_: function(mode, submitFunction) {
142 $(mode + '-profile-icon-grid').addEventListener('change', function(e) {
143 self.onIconGridSelectionChanged_(mode);
145 $(mode + '-profile-name').oninput = function(event) {
146 self.onNameChanged_(mode);
148 $(mode + '-profile-ok').onclick = function(event) {
149 OptionsPage.closeOverlay();
155 * Set the profile info used in the dialog.
156 * @param {Object} profileInfo An object of the form:
158 * name: "Profile Name",
159 * iconURL: "chrome://path/to/icon/image",
160 * filePath: "/path/to/profile/data/on/disk",
161 * isCurrentProfile: false,
164 * @param {string} mode A label that specifies the type of dialog box which
165 * is currently being viewed (i.e. 'create' or 'manage').
168 setProfileInfo_: function(profileInfo, mode) {
169 this.iconGridSelectedURL_ = profileInfo.iconURL;
170 this.profileInfo_ = profileInfo;
171 $(mode + '-profile-name').value = profileInfo.name;
172 $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL;
176 * Sets the name of the currently edited profile.
179 setProfileName_: function(name) {
180 if (this.profileInfo_)
181 this.profileInfo_.name = name;
182 $('manage-profile-name').value = name;
186 * Set an array of default icon URLs. These will be added to the grid that
187 * the user will use to choose their profile icon.
188 * @param {Array.<string>} iconURLs An array of icon URLs.
191 receiveDefaultProfileIcons_: function(iconGrid, iconURLs) {
192 $(iconGrid).dataModel = new ArrayDataModel(iconURLs);
194 if (this.profileInfo_)
195 $(iconGrid).selectedItem = this.profileInfo_.iconURL;
197 var grid = $(iconGrid);
198 // Recalculate the measured item size.
199 grid.measured_ = null;
205 * Callback to set the initial values when creating a new profile.
206 * @param {Object} profileInfo An object of the form:
208 * name: "Profile Name",
209 * iconURL: "chrome://path/to/icon/image",
213 receiveNewProfileDefaults_: function(profileInfo) {
214 ManageProfileOverlay.setProfileInfo(profileInfo, 'create');
215 $('create-profile-name-label').hidden = false;
216 $('create-profile-name').hidden = false;
217 // Trying to change the focus if this isn't the topmost overlay can
218 // instead cause the FocusManager to override another overlay's focus,
219 // e.g. if an overlay above this one is in the process of being reloaded.
220 // But the C++ handler calls this method directly on ManageProfileOverlay,
221 // so check the pageDiv to also include its subclasses (in particular
222 // CreateProfileOverlay, which has higher sub-overlays).
223 if (OptionsPage.getTopmostVisiblePage().pageDiv == this.pageDiv) {
224 // This will only have an effect if the 'create-profile-name' element
225 // is visible, i.e. if the overlay is in create mode.
226 $('create-profile-name').focus();
228 $('create-profile-ok').disabled = false;
232 * Set a dictionary of all profile names. These are used to prevent the
233 * user from naming two profiles the same.
234 * @param {Object} profileNames A dictionary of profile names.
237 receiveProfileNames_: function(profileNames) {
238 this.profileNames_ = profileNames;
242 * Callback to show the add/remove shortcut buttons when in edit mode,
243 * called by the handler as a result of the 'requestHasProfileShortcuts_'
245 * @param {boolean} hasShortcuts Whether profile has any existing shortcuts.
248 receiveHasProfileShortcuts_: function(hasShortcuts) {
249 $('add-shortcut-button').hidden = hasShortcuts;
250 $('remove-shortcut-button').hidden = !hasShortcuts;
254 * Display the error bubble, with |errorHtml| in the bubble.
255 * @param {string} errorHtml The html string to display as an error.
256 * @param {string} mode A label that specifies the type of dialog box which
257 * is currently being viewed (i.e. 'create' or 'manage').
258 * @param {boolean} disableOKButton True if the dialog's OK button should be
259 * disabled when the error bubble is shown. It will be (re-)enabled when
260 * the error bubble is hidden.
263 showErrorBubble_: function(errorHtml, mode, disableOKButton) {
264 var nameErrorEl = $(mode + '-profile-error-bubble');
265 nameErrorEl.hidden = false;
266 nameErrorEl.innerHTML = errorHtml;
269 $(mode + '-profile-ok').disabled = true;
273 * Hide the error bubble.
274 * @param {string} mode A label that specifies the type of dialog box which
275 * is currently being viewed (i.e. 'create' or 'manage').
278 hideErrorBubble_: function(mode) {
279 $(mode + '-profile-error-bubble').innerHTML = '';
280 $(mode + '-profile-error-bubble').hidden = true;
281 $(mode + '-profile-ok').disabled = false;
285 * oninput callback for <input> field.
286 * @param {string} mode A label that specifies the type of dialog box which
287 * is currently being viewed (i.e. 'create' or 'manage').
290 onNameChanged_: function(mode) {
291 var newName = $(mode + '-profile-name').value;
292 var oldName = this.profileInfo_.name;
294 // In 'create' mode, the initial name can be the name of an already
295 // existing supervised user.
296 if (newName == oldName && mode == 'manage') {
297 this.hideErrorBubble_(mode);
298 } else if (mode == 'create' &&
299 loadTimeData.getBoolean('allowCreateExistingManagedUsers') &&
300 $('create-profile-managed').checked) {
301 options.ManagedUserListData.requestExistingManagedUsers().then(
302 this.receiveExistingManagedUsers_.bind(this),
303 this.onSigninError_.bind(this));
305 this.updateOkButton_(mode);
310 * Callback which receives the list of existing managed users. Checks if the
311 * currently entered name is the name of an already existing managed user.
312 * If yes, the user is prompted to import the existing managed user, and the
313 * create button is disabled.
314 * @param {Array.<Object>} The list of existing managed users.
317 receiveExistingManagedUsers_: function(managedUsers) {
318 var newName = $('create-profile-name').value;
320 for (i = 0; i < managedUsers.length; ++i) {
321 if (managedUsers[i].name == newName &&
322 !managedUsers[i].onCurrentDevice) {
323 var errorHtml = loadTimeData.getStringF(
324 'manageProfilesExistingSupervisedUser',
325 HTMLEscape(elide(newName, /* maxLength */ 50)));
326 this.showErrorBubble_(errorHtml, 'create', true);
328 // Check if another supervised user also exists with that name.
329 var nameIsUnique = true;
331 for (j = i + 1; j < managedUsers.length; ++j) {
332 if (managedUsers[j].name == newName) {
333 nameIsUnique = false;
337 function getImportHandler(managedUser, nameIsUnique) {
339 if (managedUser.needAvatar || !nameIsUnique) {
340 OptionsPage.navigateToPage('managedUserImport');
342 chrome.send('createProfile',
343 [managedUser.name, managedUser.iconURL, false, true,
348 $('supervised-user-import').onclick =
349 getImportHandler(managedUsers[i], nameIsUnique);
350 $('create-profile-ok').disabled = true;
354 this.updateOkButton_('create');
358 * Called in case the request for the list of managed users fails because of
362 onSigninError_: function() {
363 this.updateImportExistingManagedUserLink_(false);
367 * Called to update the state of the ok button depending if the name is
368 * already used or not.
369 * @param {string} mode A label that specifies the type of dialog box which
370 * is currently being viewed (i.e. 'create' or 'manage').
373 updateOkButton_: function(mode) {
374 var newName = $(mode + '-profile-name').value;
375 if (this.profileNames_[newName] != undefined) {
377 loadTimeData.getString('manageProfilesDuplicateNameError');
378 this.showErrorBubble_(errorHtml, mode, true);
380 this.hideErrorBubble_(mode);
382 var nameIsValid = $(mode + '-profile-name').validity.valid;
383 $(mode + '-profile-ok').disabled = !nameIsValid;
388 * Called when the user clicks "OK" or hits enter. Saves the newly changed
392 submitManageChanges_: function() {
393 var name = $('manage-profile-name').value;
394 var iconURL = $('manage-profile-icon-grid').selectedItem;
396 chrome.send('setProfileIconAndName',
397 [this.profileInfo_.filePath, iconURL, name]);
398 if (name != this.profileInfo_.name)
399 options.ManagedUserListData.resetPromise();
403 * Called when the user clicks "OK" or hits enter. Creates the profile
404 * using the information in the dialog.
407 submitCreateProfile_: function() {
408 // This is visual polish: the UI to access this should be disabled for
409 // managed users, and the back end will prevent user creation anyway.
410 if (this.profileInfo_ && this.profileInfo_.isManaged)
413 this.hideErrorBubble_('create');
414 CreateProfileOverlay.updateCreateInProgress(true);
416 // Get the user's chosen name and icon, or default if they do not
417 // wish to customize their profile.
418 var name = $('create-profile-name').value;
419 var iconUrl = $('create-profile-icon-grid').selectedItem;
420 var createShortcut = $('create-shortcut').checked;
421 var isManaged = $('create-profile-managed').checked;
422 var existingManagedUserId = '';
424 // 'createProfile' is handled by the CreateProfileHandler.
425 chrome.send('createProfile',
426 [name, iconUrl, createShortcut,
427 isManaged, existingManagedUserId]);
431 * Called when the selected icon in the icon grid changes.
432 * @param {string} mode A label that specifies the type of dialog box which
433 * is currently being viewed (i.e. 'create' or 'manage').
436 onIconGridSelectionChanged_: function(mode) {
437 var iconURL = $(mode + '-profile-icon-grid').selectedItem;
438 if (!iconURL || iconURL == this.iconGridSelectedURL_)
440 this.iconGridSelectedURL_ = iconURL;
441 if (this.profileInfo_ && this.profileInfo_.filePath) {
442 chrome.send('profileIconSelectionChanged',
443 [this.profileInfo_.filePath, iconURL]);
448 * Updates the contents of the "Manage Profile" section of the dialog,
449 * and shows that section.
452 prepareForManageDialog_: function() {
453 var profileInfo = BrowserOptions.getCurrentProfile();
454 ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
455 $('manage-profile-overlay-create').hidden = true;
456 $('manage-profile-overlay-manage').hidden = false;
457 $('manage-profile-overlay-delete').hidden = true;
458 $('manage-profile-name').disabled = profileInfo.isManaged;
459 this.hideErrorBubble_('manage');
463 * Display the "Manage Profile" dialog.
466 showManageDialog_: function() {
467 this.prepareForManageDialog_();
468 OptionsPage.navigateToPage('manageProfile');
472 * Display the "Delete Profile" dialog.
473 * @param {Object} profileInfo The profile object of the profile to delete.
476 showDeleteDialog_: function(profileInfo) {
477 if (BrowserOptions.getCurrentProfile().isManaged)
480 ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
481 $('manage-profile-overlay-create').hidden = true;
482 $('manage-profile-overlay-manage').hidden = true;
483 $('manage-profile-overlay-delete').hidden = false;
484 $('delete-profile-icon').style.content =
485 imageset(profileInfo.iconURL + '@scalefactorx');
486 $('delete-profile-text').textContent =
487 loadTimeData.getStringF('deleteProfileMessage',
488 elide(profileInfo.name, /* maxLength */ 50));
489 $('delete-managed-profile-addendum').hidden = !profileInfo.isManaged;
491 // Because this dialog isn't useful when refreshing or as part of the
492 // history, don't create a history entry for it when showing.
493 OptionsPage.showPageByName('manageProfile', false);
497 * Display the "Create Profile" dialog.
500 showCreateDialog_: function() {
501 OptionsPage.navigateToPage('createProfile');
505 // Forward public APIs to private implementations.
507 'receiveDefaultProfileIcons',
508 'receiveNewProfileDefaults',
509 'receiveProfileNames',
510 'receiveHasProfileShortcuts',
516 ].forEach(function(name) {
517 ManageProfileOverlay[name] = function() {
518 var instance = ManageProfileOverlay.getInstance();
519 return instance[name + '_'].apply(instance, arguments);
523 function CreateProfileOverlay() {
524 OptionsPage.call(this, 'createProfile',
525 loadTimeData.getString('createProfileTabTitle'),
526 'manage-profile-overlay');
529 cr.addSingletonGetter(CreateProfileOverlay);
531 CreateProfileOverlay.prototype = {
532 // Inherit from ManageProfileOverlay.
533 __proto__: ManageProfileOverlay.prototype,
535 // The signed-in email address of the current profile, or empty if they're
540 canShowPage: function() {
541 return !BrowserOptions.getCurrentProfile().isManaged;
545 * Configures the overlay to the "create user" mode.
548 didShowPage: function() {
549 chrome.send('requestCreateProfileUpdate');
550 chrome.send('requestDefaultProfileIcons');
551 chrome.send('requestNewProfileDefaults');
553 $('manage-profile-overlay-create').hidden = false;
554 $('manage-profile-overlay-manage').hidden = true;
555 $('manage-profile-overlay-delete').hidden = true;
556 $('create-profile-instructions').textContent =
557 loadTimeData.getStringF('createProfileInstructions');
558 this.hideErrorBubble_();
559 this.updateCreateInProgress_(false);
561 var shortcutsEnabled = loadTimeData.getBoolean('profileShortcutsEnabled');
562 $('create-shortcut-container').hidden = !shortcutsEnabled;
563 $('create-shortcut').checked = shortcutsEnabled;
565 $('create-profile-name-label').hidden = true;
566 $('create-profile-name').hidden = true;
567 $('create-profile-ok').disabled = true;
569 $('create-profile-managed').checked = false;
570 if (loadTimeData.getBoolean('allowCreateExistingManagedUsers')) {
571 $('import-existing-managed-user-link').hidden = false;
572 $('create-profile-managed').onchange = function() {
573 ManageProfileOverlay.getInstance().onNameChanged_('create');
576 $('create-profile-managed-signed-in').disabled = true;
577 $('create-profile-managed-signed-in').hidden = true;
578 $('create-profile-managed-not-signed-in').hidden = true;
582 handleCancel: function() {
583 this.cancelCreateProfile_();
587 showErrorBubble_: function(errorHtml) {
588 ManageProfileOverlay.getInstance().showErrorBubble_(errorHtml,
594 hideErrorBubble_: function() {
595 ManageProfileOverlay.getInstance().hideErrorBubble_('create');
599 * Updates the UI when a profile create step begins or ends.
600 * Note that hideErrorBubble_() also enables the "OK" button, so it
601 * must be called before this function if both are used.
602 * @param {boolean} inProgress True if the UI should be updated to show that
603 * profile creation is now in progress.
606 updateCreateInProgress_: function(inProgress) {
607 this.createInProgress_ = inProgress;
608 this.updateCreateManagedUserCheckbox_();
610 $('create-profile-icon-grid').disabled = inProgress;
611 $('create-profile-name').disabled = inProgress;
612 $('create-shortcut').disabled = inProgress;
613 $('create-profile-ok').disabled = inProgress;
615 $('create-profile-throbber').hidden = !inProgress;
619 * Cancels the creation of the a profile. It is safe to call this even
620 * when no profile is in the process of being created.
623 cancelCreateProfile_: function() {
624 OptionsPage.closeOverlay();
625 chrome.send('cancelCreateProfile');
626 this.hideErrorBubble_();
627 this.updateCreateInProgress_(false);
631 * Shows an error message describing an error that occurred while creating
633 * Called by BrowserOptions via the BrowserOptionsHandler.
634 * @param {string} error The error message to display.
637 onError_: function(error) {
638 this.updateCreateInProgress_(false);
639 this.showErrorBubble_(error);
643 * Shows a warning message giving information while creating a new profile.
644 * Called by BrowserOptions via the BrowserOptionsHandler.
645 * @param {string} warning The warning message to display.
648 onWarning_: function(warning) {
649 this.showErrorBubble_(warning);
653 * For new supervised users, shows a confirmation page after successfully
654 * creating a new profile; otherwise, the handler will open a new window.
655 * @param {Object} profileInfo An object of the form:
657 * name: "Profile Name",
658 * filePath: "/path/to/profile/data/on/disk"
659 * isManaged: (true|false),
663 onSuccess_: function(profileInfo) {
664 this.updateCreateInProgress_(false);
665 OptionsPage.closeOverlay();
666 if (profileInfo.isManaged) {
667 options.ManagedUserListData.resetPromise();
668 profileInfo.custodianEmail = this.signedInEmail_;
669 ManagedUserCreateConfirmOverlay.setProfileInfo(profileInfo);
670 OptionsPage.showPageByName('managedUserCreateConfirm', false);
671 BrowserOptions.updateManagesSupervisedUsers(true);
676 * Updates the signed-in or not-signed-in UI when in create mode. Called by
677 * the handler in response to the 'requestCreateProfileUpdate' message.
678 * updateManagedUsersAllowed_ is expected to be called after this is, and
679 * will update additional UI elements.
680 * @param {string} email The email address of the currently signed-in user.
681 * An empty string indicates that the user is not signed in.
682 * @param {boolean} hasError Whether the user's sign-in credentials are
686 updateSignedInStatus_: function(email, hasError) {
687 this.signedInEmail_ = email;
688 this.hasError_ = hasError;
689 var isSignedIn = email !== '';
690 $('create-profile-managed-signed-in').hidden = !isSignedIn;
691 $('create-profile-managed-not-signed-in').hidden = isSignedIn;
694 var accountDetailsOutOfDate =
695 $('create-profile-managed-account-details-out-of-date-label');
696 accountDetailsOutOfDate.textContent = loadTimeData.getStringF(
697 'manageProfilesManagedAccountDetailsOutOfDate', email);
698 accountDetailsOutOfDate.hidden = !hasError;
700 $('create-profile-managed-signed-in-label').textContent =
701 loadTimeData.getStringF(
702 'manageProfilesManagedSignedInLabel', email);
703 $('create-profile-managed-signed-in-label').hidden = hasError;
705 $('create-profile-managed-sign-in-again-link').hidden = !hasError;
706 $('create-profile-managed-signed-in-learn-more-link').hidden = hasError;
709 this.updateImportExistingManagedUserLink_(isSignedIn && !hasError);
713 * Enables/disables the 'import existing managed users' link button.
714 * It also updates the button text.
715 * @param {boolean} enable True to enable the link button and
719 updateImportExistingManagedUserLink_: function(enable) {
720 var importManagedUserElement = $('import-existing-managed-user-link');
721 importManagedUserElement.disabled = !enable;
722 importManagedUserElement.textContent = enable ?
723 loadTimeData.getString('importExistingManagedUserLink') :
724 loadTimeData.getString('signInToImportManagedUsers');
728 * Sets whether creating managed users is allowed or not. Called by the
729 * handler in response to the 'requestCreateProfileUpdate' message or a
730 * change in the (policy-controlled) pref that prohibits creating managed
731 * users, after the signed-in status has been updated.
732 * @param {boolean} allowed True if creating managed users should be
736 updateManagedUsersAllowed_: function(allowed) {
737 this.managedUsersAllowed_ = allowed;
738 this.updateCreateManagedUserCheckbox_();
740 $('create-profile-managed-not-signed-in-link').hidden = !allowed;
742 $('create-profile-managed-indicator').setAttribute('controlled-by',
745 $('create-profile-managed-indicator').removeAttribute('controlled-by');
750 * Updates the status of the "create managed user" checkbox. Called from
751 * updateManagedUsersAllowed_() or updateCreateInProgress_().
752 * updateSignedInStatus_() does not call this method directly, because it
753 * will be followed by a call to updateManagedUsersAllowed_().
756 updateCreateManagedUserCheckbox_: function() {
757 $('create-profile-managed').disabled =
758 !this.managedUsersAllowed_ || this.createInProgress_ ||
759 this.signedInEmail_ == '' || this.hasError_;
763 // Forward public APIs to private implementations.
765 'cancelCreateProfile',
769 'updateCreateInProgress',
770 'updateManagedUsersAllowed',
771 'updateSignedInStatus',
772 ].forEach(function(name) {
773 CreateProfileOverlay[name] = function() {
774 var instance = CreateProfileOverlay.getInstance();
775 return instance[name + '_'].apply(instance, arguments);
781 ManageProfileOverlay: ManageProfileOverlay,
782 CreateProfileOverlay: CreateProfileOverlay,