• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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