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
5cr.define('options', function() {
6  var OptionsPage = options.OptionsPage;
7  var ArrayDataModel = cr.ui.ArrayDataModel;
8
9  /**
10   * ManageProfileOverlay class
11   * Encapsulated handling of the 'Manage profile...' overlay page.
12   * @constructor
13   * @class
14   */
15  function ManageProfileOverlay() {
16    OptionsPage.call(this, 'manageProfile',
17                     loadTimeData.getString('manageProfileTabTitle'),
18                     'manage-profile-overlay');
19  };
20
21  cr.addSingletonGetter(ManageProfileOverlay);
22
23  ManageProfileOverlay.prototype = {
24    // Inherit from OptionsPage.
25    __proto__: OptionsPage.prototype,
26
27    // Info about the currently managed/deleted profile.
28    profileInfo_: null,
29
30    // An object containing all known profile names.
31    profileNames_: {},
32
33    // The currently selected icon in the icon grid.
34    iconGridSelectedURL_: null,
35
36    /**
37     * Initialize the page.
38     */
39    initializePage: function() {
40      // Call base class implementation to start preference initialization.
41      OptionsPage.prototype.initializePage.call(this);
42
43      var self = 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));
50
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_();
55      };
56
57      $('create-profile-cancel').onclick = function(event) {
58        CreateProfileOverlay.cancelCreateProfile();
59      };
60
61      $('import-existing-managed-user-link').hidden =
62          !loadTimeData.getBoolean('allowCreateExistingManagedUsers');
63
64      $('manage-profile-cancel').onclick =
65          $('delete-profile-cancel').onclick = function(event) {
66        OptionsPage.closeOverlay();
67      };
68      $('delete-profile-ok').onclick = function(event) {
69        OptionsPage.closeOverlay();
70        if (BrowserOptions.getCurrentProfile().isManaged)
71          return;
72        chrome.send('deleteProfile', [self.profileInfo_.filePath]);
73      };
74      $('add-shortcut-button').onclick = function(event) {
75        chrome.send('addProfileShortcut', [self.profileInfo_.filePath]);
76      };
77      $('remove-shortcut-button').onclick = function(event) {
78        chrome.send('removeProfileShortcut', [self.profileInfo_.filePath]);
79      };
80
81      $('create-profile-managed-signed-in-learn-more-link').onclick =
82          function(event) {
83        OptionsPage.navigateToPage('managedUserLearnMore');
84        return false;
85      };
86
87      $('create-profile-managed-not-signed-in-link').onclick = function(event) {
88        // The signin process will open an overlay to configure sync, which
89        // would replace this overlay. It's smoother to close this one now.
90        // TODO(pamg): Move the sync-setup overlay to a higher layer so this one
91        // can stay open under it, after making sure that doesn't break anything
92        // else.
93        OptionsPage.closeOverlay();
94        SyncSetupOverlay.startSignIn();
95      };
96
97      $('create-profile-managed-sign-in-again-link').onclick = function(event) {
98        OptionsPage.closeOverlay();
99        SyncSetupOverlay.showSetupUI();
100      };
101
102      $('import-existing-managed-user-link').onclick = function(event) {
103        OptionsPage.closeOverlay();
104        OptionsPage.navigateToPage('managedUserImport');
105      };
106    },
107
108    /** @override */
109    didShowPage: function() {
110      chrome.send('requestDefaultProfileIcons');
111
112      // Just ignore the manage profile dialog on Chrome OS, they use /accounts.
113      if (!cr.isChromeOS && window.location.pathname == '/manageProfile')
114        ManageProfileOverlay.getInstance().prepareForManageDialog_();
115
116      // When editing a profile, initially hide the "add shortcut" and
117      // "remove shortcut" buttons and ask the handler which to show. It will
118      // call |receiveHasProfileShortcuts|, which will show the appropriate one.
119      $('remove-shortcut-button').hidden = true;
120      $('add-shortcut-button').hidden = true;
121
122      if (loadTimeData.getBoolean('profileShortcutsEnabled')) {
123        var profileInfo = ManageProfileOverlay.getInstance().profileInfo_;
124        chrome.send('requestHasProfileShortcuts', [profileInfo.filePath]);
125      }
126
127      var manageNameField = $('manage-profile-name');
128      // Supervised users cannot edit their names.
129      if (manageNameField.disabled)
130        $('manage-profile-ok').focus();
131      else
132        manageNameField.focus();
133    },
134
135    /**
136     * Registers event handlers that are common between create and manage modes.
137     * @param {string} mode A label that specifies the type of dialog
138     *     box which is currently being viewed (i.e. 'create' or
139     *     'manage').
140     * @param {function()} submitFunction The function that should be called
141     *     when the user chooses to submit (e.g. by clicking the OK button).
142     * @private
143     */
144    registerCommonEventHandlers_: function(mode, submitFunction) {
145      var self = this;
146      $(mode + '-profile-icon-grid').addEventListener('change', function(e) {
147        self.onIconGridSelectionChanged_(mode);
148      });
149      $(mode + '-profile-name').oninput = function(event) {
150        self.onNameChanged_(event, mode);
151      };
152      $(mode + '-profile-ok').onclick = function(event) {
153        OptionsPage.closeOverlay();
154        submitFunction();
155      };
156    },
157
158    /**
159     * Set the profile info used in the dialog.
160     * @param {Object} profileInfo An object of the form:
161     *     profileInfo = {
162     *       name: "Profile Name",
163     *       iconURL: "chrome://path/to/icon/image",
164     *       filePath: "/path/to/profile/data/on/disk",
165     *       isCurrentProfile: false,
166     *       isManaged: false
167     *     };
168     * @param {string} mode A label that specifies the type of dialog
169     *     box which is currently being viewed (i.e. 'create' or
170     *     'manage').
171     * @private
172     */
173    setProfileInfo_: function(profileInfo, mode) {
174      this.iconGridSelectedURL_ = profileInfo.iconURL;
175      this.profileInfo_ = profileInfo;
176      $(mode + '-profile-name').value = profileInfo.name;
177      $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL;
178    },
179
180    /**
181     * Sets the name of the currently edited profile.
182     * @private
183     */
184    setProfileName_: function(name) {
185      if (this.profileInfo_)
186        this.profileInfo_.name = name;
187      $('manage-profile-name').value = name;
188    },
189
190    /**
191     * Set an array of default icon URLs. These will be added to the grid that
192     * the user will use to choose their profile icon.
193     * @param {Array.<string>} iconURLs An array of icon URLs.
194     * @private
195     */
196    receiveDefaultProfileIcons_: function(iconGrid, iconURLs) {
197      $(iconGrid).dataModel = new ArrayDataModel(iconURLs);
198
199      if (this.profileInfo_)
200        $(iconGrid).selectedItem = this.profileInfo_.iconURL;
201
202      var grid = $(iconGrid);
203      // Recalculate the measured item size.
204      grid.measured_ = null;
205      grid.columns = 0;
206      grid.redraw();
207    },
208
209    /**
210     * Callback to set the initial values when creating a new profile.
211     * @param {Object} profileInfo An object of the form:
212     *     profileInfo = {
213     *       name: "Profile Name",
214     *       iconURL: "chrome://path/to/icon/image",
215     *     };
216     * @private
217     */
218    receiveNewProfileDefaults_: function(profileInfo) {
219      ManageProfileOverlay.setProfileInfo(profileInfo, 'create');
220      $('create-profile-name-label').hidden = false;
221      $('create-profile-name').hidden = false;
222      // Trying to change the focus if this isn't the topmost overlay can
223      // instead cause the FocusManager to override another overlay's focus,
224      // e.g. if an overlay above this one is in the process of being reloaded.
225      // But the C++ handler calls this method directly on ManageProfileOverlay,
226      // so check the pageDiv to also include its subclasses (in particular
227      // CreateProfileOverlay, which has higher sub-overlays).
228      if (OptionsPage.getTopmostVisiblePage().pageDiv == this.pageDiv) {
229        // This will only have an effect if the 'create-profile-name' element
230        //  is visible, i.e. if the overlay is in create mode.
231        $('create-profile-name').focus();
232      }
233      $('create-profile-ok').disabled = false;
234    },
235
236    /**
237     * Set a dictionary of all profile names. These are used to prevent the
238     * user from naming two profiles the same.
239     * @param {Object} profileNames A dictionary of profile names.
240     * @private
241     */
242    receiveProfileNames_: function(profileNames) {
243      this.profileNames_ = profileNames;
244    },
245
246    /**
247     * Callback to show the add/remove shortcut buttons when in edit mode,
248     * called by the handler as a result of the 'requestHasProfileShortcuts_'
249     * message.
250     * @param {boolean} hasShortcuts Whether profile has any existing shortcuts.
251     * @private
252     */
253    receiveHasProfileShortcuts_: function(hasShortcuts) {
254      $('add-shortcut-button').hidden = hasShortcuts;
255      $('remove-shortcut-button').hidden = !hasShortcuts;
256    },
257
258    /**
259     * Display the error bubble, with |errorText| in the bubble.
260     * @param {string} errorText The string to display as an error.
261     * @param {string} mode A label that specifies the type of dialog
262     *     box which is currently being viewed (i.e. 'create' or
263     *     'manage').
264     * @param {boolean} disableOKButton True if the dialog's OK button should be
265     *     disabled when the error bubble is shown. It will be (re-)enabled when
266     *     the error bubble is hidden.
267     * @private
268     */
269    showErrorBubble_: function(errorText, mode, disableOKButton) {
270      var nameErrorEl = $(mode + '-profile-error-bubble');
271      nameErrorEl.hidden = false;
272      nameErrorEl.textContent = errorText;
273
274      if (disableOKButton)
275        $(mode + '-profile-ok').disabled = true;
276    },
277
278    /**
279     * Hide the error bubble.
280     * @param {string} mode A label that specifies the type of dialog
281     *     box which is currently being viewed (i.e. 'create' or
282     *     'manage').
283     * @private
284     */
285    hideErrorBubble_: function(mode) {
286      $(mode + '-profile-error-bubble').hidden = true;
287      $(mode + '-profile-ok').disabled = false;
288    },
289
290    /**
291     * oninput callback for <input> field.
292     * @param {Event} event The event object.
293     * @param {string} mode A label that specifies the type of dialog
294     *     box which is currently being viewed (i.e. 'create' or
295     *     'manage').
296     * @private
297     */
298    onNameChanged_: function(event, mode) {
299      var newName = event.target.value;
300      var oldName = this.profileInfo_.name;
301
302      if (newName == oldName) {
303        this.hideErrorBubble_(mode);
304      } else if (this.profileNames_[newName] != undefined) {
305        var errorText =
306            loadTimeData.getString('manageProfilesDuplicateNameError');
307        this.showErrorBubble_(errorText, mode, true);
308      } else {
309        this.hideErrorBubble_(mode);
310
311        var nameIsValid = $(mode + '-profile-name').validity.valid;
312        $(mode + '-profile-ok').disabled = !nameIsValid;
313      }
314    },
315
316    /**
317     * Called when the user clicks "OK" or hits enter. Saves the newly changed
318     * profile info.
319     * @private
320     */
321    submitManageChanges_: function() {
322      var name = $('manage-profile-name').value;
323      var iconURL = $('manage-profile-icon-grid').selectedItem;
324
325      chrome.send('setProfileIconAndName',
326                  [this.profileInfo_.filePath, iconURL, name]);
327    },
328
329    /**
330     * Called when the user clicks "OK" or hits enter. Creates the profile
331     * using the information in the dialog.
332     * @private
333     */
334    submitCreateProfile_: function() {
335      // This is visual polish: the UI to access this should be disabled for
336      // managed users, and the back end will prevent user creation anyway.
337      if (this.profileInfo_ && this.profileInfo_.isManaged)
338        return;
339
340      this.hideErrorBubble_('create');
341      CreateProfileOverlay.updateCreateInProgress(true);
342
343      // Get the user's chosen name and icon, or default if they do not
344      // wish to customize their profile.
345      var name = $('create-profile-name').value;
346      var iconUrl = $('create-profile-icon-grid').selectedItem;
347      var createShortcut = $('create-shortcut').checked;
348      var isManaged = $('create-profile-managed').checked;
349      var existingManagedUserId = '';
350
351      // 'createProfile' is handled by the CreateProfileHandler.
352      chrome.send('createProfile',
353                  [name, iconUrl, createShortcut,
354                   isManaged, existingManagedUserId]);
355    },
356
357    /**
358     * Called when the selected icon in the icon grid changes.
359     * @param {string} mode A label that specifies the type of dialog
360     *     box which is currently being viewed (i.e. 'create' or
361     *     'manage').
362     * @private
363     */
364    onIconGridSelectionChanged_: function(mode) {
365      var iconURL = $(mode + '-profile-icon-grid').selectedItem;
366      if (!iconURL || iconURL == this.iconGridSelectedURL_)
367        return;
368      this.iconGridSelectedURL_ = iconURL;
369      if (this.profileInfo_ && this.profileInfo_.filePath) {
370        chrome.send('profileIconSelectionChanged',
371                    [this.profileInfo_.filePath, iconURL]);
372      }
373    },
374
375    /**
376     * Updates the contents of the "Manage Profile" section of the dialog,
377     * and shows that section.
378     * @private
379     */
380    prepareForManageDialog_: function() {
381      var profileInfo = BrowserOptions.getCurrentProfile();
382      ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
383      $('manage-profile-overlay-create').hidden = true;
384      $('manage-profile-overlay-manage').hidden = false;
385      $('manage-profile-overlay-delete').hidden = true;
386      $('manage-profile-name').disabled = profileInfo.isManaged;
387      this.hideErrorBubble_('manage');
388    },
389
390    /**
391     * Display the "Manage Profile" dialog.
392     * @private
393     */
394    showManageDialog_: function() {
395      this.prepareForManageDialog_();
396      OptionsPage.navigateToPage('manageProfile');
397    },
398
399    /**
400     * Display the "Delete Profile" dialog.
401     * @param {Object} profileInfo The profile object of the profile to delete.
402     * @private
403     */
404    showDeleteDialog_: function(profileInfo) {
405      if (BrowserOptions.getCurrentProfile().isManaged)
406        return;
407
408      ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
409      $('manage-profile-overlay-create').hidden = true;
410      $('manage-profile-overlay-manage').hidden = true;
411      $('manage-profile-overlay-delete').hidden = false;
412      $('delete-profile-icon').style.content =
413          imageset(profileInfo.iconURL + '@scalefactorx');
414      $('delete-profile-text').textContent =
415          loadTimeData.getStringF('deleteProfileMessage', profileInfo.name);
416      $('delete-managed-profile-addendum').hidden = !profileInfo.isManaged;
417
418      // Because this dialog isn't useful when refreshing or as part of the
419      // history, don't create a history entry for it when showing.
420      OptionsPage.showPageByName('manageProfile', false);
421    },
422
423    /**
424     * Display the "Create Profile" dialog.
425     * @private
426     */
427    showCreateDialog_: function() {
428      OptionsPage.navigateToPage('createProfile');
429    },
430  };
431
432  // Forward public APIs to private implementations.
433  [
434    'receiveDefaultProfileIcons',
435    'receiveNewProfileDefaults',
436    'receiveProfileNames',
437    'receiveHasProfileShortcuts',
438    'setProfileInfo',
439    'setProfileName',
440    'showManageDialog',
441    'showDeleteDialog',
442    'showCreateDialog',
443  ].forEach(function(name) {
444    ManageProfileOverlay[name] = function() {
445      var instance = ManageProfileOverlay.getInstance();
446      return instance[name + '_'].apply(instance, arguments);
447    };
448  });
449
450  function CreateProfileOverlay() {
451    OptionsPage.call(this, 'createProfile',
452                     loadTimeData.getString('createProfileTabTitle'),
453                     'manage-profile-overlay');
454  };
455
456  cr.addSingletonGetter(CreateProfileOverlay);
457
458  CreateProfileOverlay.prototype = {
459    // Inherit from ManageProfileOverlay.
460    __proto__: ManageProfileOverlay.prototype,
461
462    // The signed-in email address of the current profile, or empty if they're
463    // not signed in.
464    signedInEmail_: '',
465
466    /** @override */
467    canShowPage: function() {
468      return !BrowserOptions.getCurrentProfile().isManaged;
469    },
470
471    /**
472     * Configures the overlay to the "create user" mode.
473     * @override
474     */
475    didShowPage: function() {
476      chrome.send('requestCreateProfileUpdate');
477      chrome.send('requestDefaultProfileIcons');
478      chrome.send('requestNewProfileDefaults');
479
480      $('manage-profile-overlay-create').hidden = false;
481      $('manage-profile-overlay-manage').hidden = true;
482      $('manage-profile-overlay-delete').hidden = true;
483      $('create-profile-instructions').textContent =
484         loadTimeData.getStringF('createProfileInstructions');
485      this.hideErrorBubble_();
486      this.updateCreateInProgress_(false);
487
488      var shortcutsEnabled = loadTimeData.getBoolean('profileShortcutsEnabled');
489      $('create-shortcut-container').hidden = !shortcutsEnabled;
490      $('create-shortcut').checked = shortcutsEnabled;
491
492      $('create-profile-name-label').hidden = true;
493      $('create-profile-name').hidden = true;
494      $('create-profile-ok').disabled = true;
495
496      $('create-profile-managed').checked = false;
497      $('create-profile-managed-signed-in').disabled = true;
498      $('create-profile-managed-signed-in').hidden = true;
499      $('create-profile-managed-not-signed-in').hidden = true;
500    },
501
502    /** @override */
503    handleCancel: function() {
504      this.cancelCreateProfile_();
505    },
506
507    /** @override */
508    showErrorBubble_: function(errorText) {
509      ManageProfileOverlay.getInstance().showErrorBubble_(errorText,
510                                                          'create',
511                                                          false);
512    },
513
514    /** @override */
515    hideErrorBubble_: function() {
516      ManageProfileOverlay.getInstance().hideErrorBubble_('create');
517    },
518
519    /**
520     * Updates the UI when a profile create step begins or ends.
521     * Note that hideErrorBubble_() also enables the "OK" button, so it
522     * must be called before this function if both are used.
523     * @param {boolean} inProgress True if the UI should be updated to show that
524     *     profile creation is now in progress.
525     * @private
526     */
527    updateCreateInProgress_: function(inProgress) {
528      this.createInProgress_ = inProgress;
529      this.updateCreateManagedUserCheckbox_();
530
531      $('create-profile-icon-grid').disabled = inProgress;
532      $('create-profile-name').disabled = inProgress;
533      $('create-shortcut').disabled = inProgress;
534      $('create-profile-ok').disabled = inProgress;
535
536      $('create-profile-throbber').hidden = !inProgress;
537    },
538
539    /**
540     * Cancels the creation of the a profile. It is safe to call this even
541     * when no profile is in the process of being created.
542     * @private
543     */
544    cancelCreateProfile_: function() {
545      OptionsPage.closeOverlay();
546      chrome.send('cancelCreateProfile');
547      this.hideErrorBubble_();
548      this.updateCreateInProgress_(false);
549    },
550
551    /**
552     * Shows an error message describing an error that occurred while creating
553     * a new profile.
554     * Called by BrowserOptions via the BrowserOptionsHandler.
555     * @param {string} error The error message to display.
556     * @private
557     */
558    onError_: function(error) {
559      this.updateCreateInProgress_(false);
560      this.showErrorBubble_(error);
561    },
562
563    /**
564     * Shows a warning message giving information while creating a new profile.
565     * Called by BrowserOptions via the BrowserOptionsHandler.
566     * @param {string} warning The warning message to display.
567     * @private
568     */
569    onWarning_: function(warning) {
570      this.showErrorBubble_(warning);
571    },
572
573    /**
574     * For new supervised users, shows a confirmation page after successfully
575     * creating a new profile; otherwise, the handler will open a new window.
576     * @param {Object} profileInfo An object of the form:
577     *     profileInfo = {
578     *       name: "Profile Name",
579     *       filePath: "/path/to/profile/data/on/disk"
580     *       isManaged: (true|false),
581     *     };
582     * @private
583     */
584    onSuccess_: function(profileInfo) {
585      this.updateCreateInProgress_(false);
586      OptionsPage.closeOverlay();
587      if (profileInfo.isManaged) {
588        profileInfo.custodianEmail = this.signedInEmail_;
589        ManagedUserCreateConfirmOverlay.setProfileInfo(profileInfo);
590        OptionsPage.showPageByName('managedUserCreateConfirm', false);
591        BrowserOptions.updateManagesSupervisedUsers(true);
592      }
593    },
594
595    /**
596     * Updates the signed-in or not-signed-in UI when in create mode. Called by
597     * the handler in response to the 'requestCreateProfileUpdate' message.
598     * updateManagedUsersAllowed_ is expected to be called after this is, and
599     * will update additional UI elements.
600     * @param {string} email The email address of the currently signed-in user.
601     *     An empty string indicates that the user is not signed in.
602     * @param {boolean} hasError Whether the user's sign-in credentials are
603     *     still valid.
604     * @private
605     */
606    updateSignedInStatus_: function(email, hasError) {
607      this.signedInEmail_ = email;
608      this.hasError_ = hasError;
609      var isSignedIn = email !== '';
610      $('create-profile-managed-signed-in').hidden = !isSignedIn;
611      $('create-profile-managed-not-signed-in').hidden = isSignedIn;
612
613      if (isSignedIn) {
614        var accountDetailsOutOfDate =
615            $('create-profile-managed-account-details-out-of-date-label');
616        accountDetailsOutOfDate.textContent = loadTimeData.getStringF(
617            'manageProfilesManagedAccountDetailsOutOfDate', email);
618        accountDetailsOutOfDate.hidden = !hasError;
619
620        $('create-profile-managed-signed-in-label').textContent =
621            loadTimeData.getStringF(
622                'manageProfilesManagedSignedInLabel', email);
623        $('create-profile-managed-signed-in-label').hidden = hasError;
624
625        $('create-profile-managed-sign-in-again-link').hidden = !hasError;
626        $('create-profile-managed-signed-in-learn-more-link').hidden = hasError;
627      }
628
629      this.updateImportExistingManagedUserLink_(isSignedIn && !hasError);
630    },
631
632    /**
633     * Enables/disables the 'import existing managed users' link button.
634     * It also updates the button text.
635     * @param {boolean} enable True to enable the link button and
636     *     false otherwise.
637     * @private
638     */
639    updateImportExistingManagedUserLink_: function(enable) {
640      var importManagedUserElement = $('import-existing-managed-user-link');
641      importManagedUserElement.disabled = !enable;
642      importManagedUserElement.textContent = enable ?
643          loadTimeData.getString('importExistingManagedUserLink') :
644          loadTimeData.getString('signInToImportManagedUsers');
645    },
646
647    /**
648     * Sets whether creating managed users is allowed or not. Called by the
649     * handler in response to the 'requestCreateProfileUpdate' message or a
650     * change in the (policy-controlled) pref that prohibits creating managed
651     * users, after the signed-in status has been updated.
652     * @param {boolean} allowed True if creating managed users should be
653     *     allowed.
654     * @private
655     */
656    updateManagedUsersAllowed_: function(allowed) {
657      this.managedUsersAllowed_ = allowed;
658      this.updateCreateManagedUserCheckbox_();
659
660      $('create-profile-managed-not-signed-in-link').hidden = !allowed;
661      if (!allowed) {
662        $('create-profile-managed-indicator').setAttribute('controlled-by',
663                                                           'policy');
664      } else {
665        $('create-profile-managed-indicator').removeAttribute('controlled-by');
666      }
667    },
668
669    /**
670     * Updates the status of the "create managed user" checkbox. Called from
671     * updateManagedUsersAllowed_() or updateCreateInProgress_().
672     * updateSignedInStatus_() does not call this method directly, because it
673     * will be followed by a call to updateManagedUsersAllowed_().
674     * @private
675     */
676    updateCreateManagedUserCheckbox_: function() {
677      $('create-profile-managed').disabled =
678          !this.managedUsersAllowed_ || this.createInProgress_ ||
679          this.signedInEmail_ == '' || this.hasError_;
680    },
681  };
682
683  // Forward public APIs to private implementations.
684  [
685    'cancelCreateProfile',
686    'onError',
687    'onSuccess',
688    'onWarning',
689    'updateCreateInProgress',
690    'updateManagedUsersAllowed',
691    'updateSignedInStatus',
692  ].forEach(function(name) {
693    CreateProfileOverlay[name] = function() {
694      var instance = CreateProfileOverlay.getInstance();
695      return instance[name + '_'].apply(instance, arguments);
696    };
697  });
698
699  // Export
700  return {
701    ManageProfileOverlay: ManageProfileOverlay,
702    CreateProfileOverlay: CreateProfileOverlay,
703  };
704});
705