Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / login / screen_supervised_user_creation.js
blobcc0bc952e8e39b79efe49bd3dacedbe3fb8a5ddb
1 // Copyright 2014 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 /**
6  * @fileoverview Legacy supervised user creation flow screen.
7  */
9 login.createScreen('SupervisedUserCreationScreen',
10                    'supervised-user-creation', function() {
11   var MAX_NAME_LENGTH = 50;
12   var UserImagesGrid = options.UserImagesGrid;
13   var ButtonImages = UserImagesGrid.ButtonImages;
15   var ManagerPod = cr.ui.define(function() {
16     var node = $('supervised-user-creation-manager-template').cloneNode(true);
17     node.removeAttribute('id');
18     node.removeAttribute('hidden');
19     return node;
20   });
22   ManagerPod.userImageSalt_ = {};
24   /**
25    * UI element for displaying single account in list of possible managers for
26    * new supervised user.
27    * @type {Object}
28    */
29   ManagerPod.prototype = {
30     __proto__: HTMLDivElement.prototype,
32     /** @override */
33     decorate: function() {
34       // Mousedown has to be used instead of click to be able to prevent 'focus'
35       // event later.
36       this.addEventListener('mousedown',
37                             this.handleMouseDown_.bind(this));
38       var screen = $('supervised-user-creation');
39       var managerPod = this;
40       var managerPodList = screen.managerList_;
41       var hideManagerPasswordError = function(element) {
42         managerPod.passwordElement.classList.remove('password-error');
43         $('bubble').hide();
44       };
46       screen.configureTextInput(
47           this.passwordElement,
48           screen.updateNextButtonForManager_.bind(screen),
49           screen.validIfNotEmpty_.bind(screen),
50           function(element) {
51             screen.getScreenButton('next').focus();
52           },
53           hideManagerPasswordError);
55       this.passwordElement.addEventListener('keydown', function(e) {
56         switch (e.keyIdentifier) {
57           case 'Up':
58             managerPodList.selectNextPod(-1);
59             e.stopPropagation();
60             break;
61           case 'Down':
62             managerPodList.selectNextPod(+1);
63             e.stopPropagation();
64             break;
65         }
66       });
67     },
69     /**
70      * Updates UI elements from user data.
71      */
72     update: function() {
73       this.imageElement.src = 'chrome://userimage/' + this.user.username +
74           '?id=' + ManagerPod.userImageSalt_[this.user.username];
76       this.nameElement.textContent = this.user.displayName;
77       this.emailElement.textContent = this.user.emailAddress;
78     },
80     showPasswordError: function() {
81       this.passwordElement.classList.add('password-error');
82       $('bubble').showTextForElement(
83           this.passwordElement,
84           loadTimeData.getString(
85               'createSupervisedUserWrongManagerPasswordText'),
86           cr.ui.Bubble.Attachment.BOTTOM,
87           24, 4);
88     },
90     /**
91      * Brings focus to password field.
92      */
93     focusInput: function() {
94       this.passwordElement.focus();
95     },
97     /**
98      * Gets image element.
99      * @type {!HTMLImageElement}
100      */
101     get imageElement() {
102       return this.querySelector('.supervised-user-creation-manager-image');
103     },
105     /**
106      * Gets name element.
107      * @type {!HTMLDivElement}
108      */
109     get nameElement() {
110       return this.querySelector('.supervised-user-creation-manager-name');
111     },
113     /**
114      * Gets e-mail element.
115      * @type {!HTMLDivElement}
116      */
117     get emailElement() {
118       return this.querySelector('.supervised-user-creation-manager-email');
119     },
121     /**
122      * Gets password element.
123      * @type {!HTMLDivElement}
124      */
125     get passwordElement() {
126       return this.querySelector('.supervised-user-creation-manager-password');
127     },
129     /**
130      * Gets password enclosing block.
131      * @type {!HTMLDivElement}
132      */
133     get passwordBlock() {
134       return this.querySelector('.password-block');
135     },
137     /** @override */
138     handleMouseDown_: function(e) {
139       this.parentNode.selectPod(this);
140       // Prevent default so that we don't trigger 'focus' event.
141       e.preventDefault();
142     },
144     /**
145      * The user that this pod represents.
146      * @type {!Object}
147      */
148     user_: undefined,
149     get user() {
150       return this.user_;
151     },
152     set user(userDict) {
153       this.user_ = userDict;
154       this.update();
155     },
156   };
158   var ManagerPodList = cr.ui.define('div');
160   /**
161    * UI element for selecting manager account for new supervised user.
162    * @type {Object}
163    */
164   ManagerPodList.prototype = {
165     __proto__: HTMLDivElement.prototype,
167     selectedPod_: null,
169     /** @override */
170     decorate: function() {
171     },
173     /**
174      * Returns all the pods in this pod list.
175      * @type {NodeList}
176      */
177     get pods() {
178       return this.children;
179     },
181     addPod: function(manager) {
182       var managerPod = new ManagerPod({user: manager});
183       this.appendChild(managerPod);
184       managerPod.update();
185     },
187     clearPods: function() {
188       this.innerHTML = '';
189       this.selectedPod_ = null;
190     },
192     selectPod: function(podToSelect) {
193       if ((this.selectedPod_ == podToSelect) && !!podToSelect) {
194         podToSelect.focusInput();
195         return;
196       }
197       this.selectedPod_ = podToSelect;
198       for (var i = 0, pod; pod = this.pods[i]; ++i) {
199         if (pod != podToSelect) {
200           pod.classList.remove('focused');
201           pod.passwordElement.value = '';
202           pod.passwordBlock.hidden = true;
203         }
204       }
205       if (!podToSelect)
206         return;
207       podToSelect.classList.add('focused');
208       podToSelect.passwordBlock.hidden = false;
209       podToSelect.passwordElement.value = '';
210       podToSelect.focusInput();
211       chrome.send('managerSelectedOnSupervisedUserCreationFlow',
212           [podToSelect.user.username]);
213     },
215     /**
216      * Select pod next to currently selected one in given |direction|.
217      * @param {integer} direction - +1 for selecting pod below current, -1 for
218      *     selecting pod above current.
219      * @type {boolean} returns if selected pod has changed.
220      */
221     selectNextPod: function(direction) {
222       if (!this.selectedPod_)
223         return false;
224       var index = -1;
225       for (var i = 0, pod; pod = this.pods[i]; ++i) {
226         if (pod == this.selectedPod_) {
227           index = i;
228           break;
229         }
230       }
231       if (-1 == index)
232         return false;
233       index = index + direction;
234       if (index < 0 || index >= this.pods.length)
235         return false;
236       this.selectPod(this.pods[index]);
237       return true;
238     }
239   };
241   var ImportPod = cr.ui.define(function() {
242     var node = $('supervised-user-creation-import-template').cloneNode(true);
243     node.removeAttribute('id');
244     node.removeAttribute('hidden');
245     return node;
246   });
248   /**
249    * UI element for displaying single supervised user in list of possible users
250    * for importing existing users.
251    * @type {Object}
252    */
253   ImportPod.prototype = {
254     __proto__: HTMLDivElement.prototype,
256     /** @override */
257     decorate: function() {
258       // Mousedown has to be used instead of click to be able to prevent 'focus'
259       // event later.
260       this.addEventListener('mousedown', this.handleMouseDown_.bind(this));
261       var screen = $('supervised-user-creation');
262       var importList = screen.importList_;
263     },
265     /**
266      * Updates UI elements from user data.
267      */
268     update: function() {
269       this.imageElement.src = this.user.avatarurl;
270       this.nameElement.textContent = this.user.name;
271       if (this.user.exists) {
272         if (this.user.conflict == 'imported') {
273           this.nameElement.textContent =
274               loadTimeData.getStringF('importUserExists', this.user.name);
275         } else {
276           this.nameElement.textContent =
277               loadTimeData.getStringF('importUsernameExists', this.user.name);
278         }
279       }
280       this.classList.toggle('imported', this.user.exists);
281     },
283     /**
284      * Gets image element.
285      * @type {!HTMLImageElement}
286      */
287     get imageElement() {
288       return this.querySelector('.import-pod-image');
289     },
291     /**
292      * Gets name element.
293      * @type {!HTMLDivElement}
294      */
295     get nameElement() {
296       return this.querySelector('.import-pod-name');
297     },
299     /** @override */
300     handleMouseDown_: function(e) {
301       this.parentNode.selectPod(this);
302       // Prevent default so that we don't trigger 'focus' event.
303       e.preventDefault();
304     },
306     /**
307      * The user that this pod represents.
308      * @type {Object}
309      */
310     user_: undefined,
312     get user() {
313       return this.user_;
314     },
316     set user(userDict) {
317       this.user_ = userDict;
318       this.update();
319     },
320   };
322   var ImportPodList = cr.ui.define('div');
324   /**
325    * UI element for selecting existing supervised user for import.
326    * @type {Object}
327    */
328   ImportPodList.prototype = {
329     __proto__: HTMLDivElement.prototype,
331     selectedPod_: null,
333     /** @override */
334     decorate: function() {
335       this.setAttribute('tabIndex', 0);
336       this.classList.add('nofocus');
337       var importList = this;
338       var screen = $('supervised-user-creation');
340       this.addEventListener('focus', function(e) {
341         if (importList.selectedPod_ == null) {
342           if (importList.pods.length > 0)
343             importList.selectPod(importList.pods[0]);
344         }
345       });
347       this.addEventListener('keydown', function(e) {
348         switch (e.keyIdentifier) {
349           case 'Up':
350             importList.selectNextPod(-1);
351             e.stopPropagation();
352             break;
353           case 'Enter':
354             if (importList.selectedPod_ != null)
355               screen.importSupervisedUser_();
356             e.stopPropagation();
357             break;
358           case 'Down':
359             importList.selectNextPod(+1);
360             e.stopPropagation();
361             break;
362         }
363       });
364     },
366     /**
367      * Returns all the pods in this pod list.
368      * @type {NodeList}
369      */
370     get pods() {
371       return this.children;
372     },
374     /**
375      * Returns selected pod.
376      * @type {Node}
377      */
378     get selectedPod() {
379       return this.selectedPod_;
380     },
382     addPod: function(user) {
383       var importPod = new ImportPod({user: user});
384       this.appendChild(importPod);
385       importPod.update();
386     },
388     clearPods: function() {
389       this.innerHTML = '';
390       this.selectedPod_ = null;
391     },
393     scrollIntoView: function(pod) {
394       scroller = this.parentNode;
395       var itemHeight = pod.getBoundingClientRect().height;
396       var scrollTop = scroller.scrollTop;
397       var top = pod.offsetTop - scroller.offsetTop;
398       var clientHeight = scroller.clientHeight;
400       var self = scroller;
402       // Function to adjust the tops of viewport and row.
403       function scrollToAdjustTop() {
404         self.scrollTop = top;
405         return true;
406       };
407       // Function to adjust the bottoms of viewport and row.
408       function scrollToAdjustBottom() {
409         var cs = getComputedStyle(self);
410         var paddingY = parseInt(cs.paddingTop, 10) +
411                        parseInt(cs.paddingBottom, 10);
413         if (top + itemHeight > scrollTop + clientHeight - paddingY) {
414           self.scrollTop = top + itemHeight - clientHeight + paddingY;
415           return true;
416         }
417         return false;
418       };
420       // Check if the entire of given indexed row can be shown in the viewport.
421       if (itemHeight <= clientHeight) {
422         if (top < scrollTop)
423           return scrollToAdjustTop();
424         if (scrollTop + clientHeight < top + itemHeight)
425           return scrollToAdjustBottom();
426       } else {
427         if (scrollTop < top)
428           return scrollToAdjustTop();
429         if (top + itemHeight < scrollTop + clientHeight)
430           return scrollToAdjustBottom();
431       }
432       return false;
433     },
435     /**
436      * @param {Element} podToSelect - pod to select, can be null.
437      */
438     selectPod: function(podToSelect) {
439       if ((this.selectedPod_ == podToSelect) && !!podToSelect) {
440         return;
441       }
442       this.selectedPod_ = podToSelect;
443       for (var i = 0; i < this.pods.length; i++) {
444         var pod = this.pods[i];
445         if (pod != podToSelect)
446           pod.classList.remove('focused');
447       }
448       if (!podToSelect)
449         return;
450       podToSelect.classList.add('focused');
451       podToSelect.focus();
452       var screen = $('supervised-user-creation');
453       if (!this.selectedPod_) {
454         screen.getScreenButton('import').disabled = true;
455       } else {
456         screen.getScreenButton('import').disabled =
457             this.selectedPod_.user.exists;
458         if (!this.selectedPod_.user.exists) {
459           chrome.send('userSelectedForImportInSupervisedUserCreationFlow',
460                       [podToSelect.user.id]);
461         }
462       }
463     },
465     selectNextPod: function(direction) {
466       if (!this.selectedPod_)
467         return false;
468       var index = -1;
469       for (var i = 0, pod; pod = this.pods[i]; ++i) {
470         if (pod == this.selectedPod_) {
471           index = i;
472           break;
473         }
474       }
475       if (-1 == index)
476         return false;
477       index = index + direction;
478       if (index < 0 || index >= this.pods.length)
479         return false;
480       this.selectPod(this.pods[index]);
481       return true;
482     },
484     selectUser: function(user_id) {
485       for (var i = 0, pod; pod = this.pods[i]; ++i) {
486         if (pod.user.id == user_id) {
487           this.selectPod(pod);
488           this.scrollIntoView(pod);
489           break;
490         }
491       }
492     },
493   };
495   return {
496     EXTERNAL_API: [
497       'loadManagers',
498       'setCameraPresent',
499       'setDefaultImages',
500       'setExistingSupervisedUsers',
501       'showErrorPage',
502       'showIntroPage',
503       'showManagerPage',
504       'showManagerPasswordError',
505       'showPage',
506       'showPasswordError',
507       'showProgress',
508       'showStatusError',
509       'showTutorialPage',
510       'showUsernamePage',
511       'supervisedUserNameError',
512       'supervisedUserNameOk',
513       'supervisedUserSuggestImport',
514     ],
516     lastVerifiedName_: null,
517     lastIncorrectUserName_: null,
518     managerList_: null,
519     importList_: null,
521     currentPage_: null,
522     imagesRequested_: false,
524     // Contains data that can be auto-shared with handler.
525     context_: {},
527     /** @override */
528     decorate: function() {
529       this.managerList_ = new ManagerPodList();
530       $('supervised-user-creation-managers-pane').appendChild(
531           this.managerList_);
533       this.importList_ = new ImportPodList();
534       $('supervised-user-creation-import-pane').appendChild(this.importList_);
536       var userNameField = $('supervised-user-creation-name');
537       var passwordField = $('supervised-user-creation-password');
538       var password2Field = $('supervised-user-creation-password-confirm');
540       var creationScreen = this;
542       var hideUserPasswordError = function(element) {
543         $('bubble').hide();
544         $('supervised-user-creation-password').classList.remove(
545             'password-error');
546       };
548       this.configureTextInput(userNameField,
549                               this.checkUserName_.bind(this),
550                               this.validIfNotEmpty_.bind(this),
551                               function(element) {
552                                 passwordField.focus();
553                               },
554                               this.clearUserNameError_.bind(this));
555       this.configureTextInput(passwordField,
556                               this.updateNextButtonForUser_.bind(this),
557                               this.validIfNotEmpty_.bind(this),
558                               function(element) {
559                                 password2Field.focus();
560                               },
561                               hideUserPasswordError);
562       this.configureTextInput(password2Field,
563                               this.updateNextButtonForUser_.bind(this),
564                               this.validIfNotEmpty_.bind(this),
565                               function(element) {
566                                 creationScreen.getScreenButton('next').focus();
567                               },
568                               hideUserPasswordError);
570       this.getScreenButton('error').addEventListener('click', function(e) {
571         creationScreen.handleErrorButtonPressed_();
572         e.stopPropagation();
573       });
575       /*
576       TODO(antrim) : this is an explicit code duplications with UserImageScreen.
577       It should be removed by issue 251179.
578       */
579       var imageGrid = this.getScreenElement('image-grid');
580       UserImagesGrid.decorate(imageGrid);
582       // Preview image will track the selected item's URL.
583       var previewElement = this.getScreenElement('image-preview');
584       previewElement.oncontextmenu = function(e) { e.preventDefault(); };
586       imageGrid.previewElement = previewElement;
587       imageGrid.selectionType = 'default';
588       imageGrid.flipPhotoElement = this.getScreenElement('flip-photo');
590       imageGrid.addEventListener('activate',
591                                  this.handleActivate_.bind(this));
592       imageGrid.addEventListener('select',
593                                  this.handleSelect_.bind(this));
594       imageGrid.addEventListener('phototaken',
595                                  this.handlePhotoTaken_.bind(this));
596       imageGrid.addEventListener('photoupdated',
597                                  this.handlePhotoUpdated_.bind(this));
598       // Set the title for camera item in the grid.
599       imageGrid.setCameraTitles(
600           loadTimeData.getString('takePhoto'),
601           loadTimeData.getString('photoFromCamera'));
603       this.getScreenElement('take-photo').addEventListener(
604           'click', this.handleTakePhoto_.bind(this));
605       this.getScreenElement('discard-photo').addEventListener(
606           'click', this.handleDiscardPhoto_.bind(this));
608       // Toggle 'animation' class for the duration of WebKit transition.
609       this.getScreenElement('flip-photo').addEventListener(
610           'click', this.handleFlipPhoto_.bind(this));
611       this.getScreenElement('image-stream-crop').addEventListener(
612           'webkitTransitionEnd', function(e) {
613             previewElement.classList.remove('animation');
614           });
615       this.getScreenElement('image-preview-img').addEventListener(
616           'webkitTransitionEnd', function(e) {
617             previewElement.classList.remove('animation');
618           });
620       $('supervised-user-creation-close-button-item').addEventListener(
621           'click', function(e) {
622             this.cancel();
623             e.preventDefault();
624           }.bind(this));
625     },
627     buttonIds: [],
629     /**
630      * Creates button for adding to controls.
631      * @param {string} buttonId -- id for button, have to be unique within
632      *   screen. Actual id will be prefixed with screen name and appended with
633      *   '-button'. Use getScreenButton(buttonId) to find it later.
634      * @param {string} i18nPrefix -- screen prefix for i18n values.
635      * @param {function} callback -- will be called on button press with
636      *   buttonId parameter.
637      * @param {array} pages -- list of pages where this button should be
638      *   displayed.
639      * @param {array} classes -- list of additional CSS classes for button.
640      */
641     makeButton: function(buttonId, i18nPrefix, callback, pages, classes) {
642       var capitalizedId = buttonId.charAt(0).toUpperCase() + buttonId.slice(1);
643       this.buttonIds.push(buttonId);
644       var result = this.ownerDocument.createElement('button');
645       result.id = this.name() + '-' + buttonId + '-button';
646       result.classList.add('screen-control-button');
647       for (var i = 0; i < classes.length; i++) {
648         result.classList.add(classes[i]);
649       }
650       result.textContent = loadTimeData.
651           getString(i18nPrefix + capitalizedId + 'ButtonTitle');
652       result.addEventListener('click', function(e) {
653         callback(buttonId);
654         e.stopPropagation();
655       });
656       result.pages = pages;
657       return result;
658     },
660     /**
661      * Simple validator for |configureTextInput|.
662      * Element is considered valid if it has any text.
663      * @param {Element} element - element to be validated.
664      * @return {boolean} - true, if element has any text.
665      */
666     validIfNotEmpty_: function(element) {
667       return (element.value.length > 0);
668     },
670     /**
671      * Configure text-input |element|.
672      * @param {Element} element - element to be configured.
673      * @param {function(element)} inputChangeListener - function that will be
674      *    called upon any button press/release.
675      * @param {function(element)} validator - function that will be called when
676      *    Enter is pressed. If it returns |true| then advance to next element.
677      * @param {function(element)} moveFocus - function that will determine next
678      *    element and move focus to it.
679      * @param {function(element)} errorHider - function that is called upon
680      *    every button press, so that any associated error can be hidden.
681      */
682     configureTextInput: function(element,
683                                  inputChangeListener,
684                                  validator,
685                                  moveFocus,
686                                  errorHider) {
687       element.addEventListener('keydown', function(e) {
688         if (e.keyIdentifier == 'Enter') {
689           var dataValid = true;
690           if (validator)
691             dataValid = validator(element);
692           if (!dataValid) {
693             element.focus();
694           } else {
695             if (moveFocus)
696               moveFocus(element);
697           }
698           e.stopPropagation();
699           return;
700         }
701         if (errorHider)
702           errorHider(element);
703         if (inputChangeListener)
704           inputChangeListener(element);
705       });
706       element.addEventListener('keyup', function(e) {
707         if (inputChangeListener)
708           inputChangeListener(element);
709       });
710     },
712     /**
713      * Makes element from template.
714      * @param {string} templateId -- template will be looked up within screen
715      * by class with name "template-<templateId>".
716      * @param {string} elementId -- id for result, uinque within screen. Actual
717      *   id will be prefixed with screen name. Use getScreenElement(id) to find
718      *   it later.
719      */
720     makeFromTemplate: function(templateId, elementId) {
721       var templateClassName = 'template-' + templateId;
722       var templateNode = this.querySelector('.' + templateClassName);
723       var screenPrefix = this.name() + '-';
724       var result = templateNode.cloneNode(true);
725       result.classList.remove(templateClassName);
726       result.id = screenPrefix + elementId;
727       return result;
728     },
730     /**
731      * @param {string} buttonId -- id of button to be found,
732      * @return {Element} button created by makeButton with given buttonId.
733      */
734     getScreenButton: function(buttonId) {
735       var fullId = this.name() + '-' + buttonId + '-button';
736       return this.getScreenElement(buttonId + '-button');
737     },
739     /**
740      * @param {string} elementId -- id of element to be found,
741      * @return {Element} button created by makeFromTemplate with elementId.
742      */
743     getScreenElement: function(elementId) {
744       var fullId = this.name() + '-' + elementId;
745       return $(fullId);
746     },
748     /**
749      * Screen controls.
750      * @type {!Array} Array of Buttons.
751      */
752     get buttons() {
753       var links = this.ownerDocument.createElement('div');
754       var buttons = this.ownerDocument.createElement('div');
755       links.classList.add('controls-links');
756       buttons.classList.add('controls-buttons');
758       var importLink = this.makeFromTemplate('import-supervised-user-link',
759                                              'import-link');
760       importLink.hidden = true;
761       links.appendChild(importLink);
763       var linkElement = importLink.querySelector('.signin-link');
764       linkElement.addEventListener('click',
765           this.importLinkPressed_.bind(this));
767       var createLink = this.makeFromTemplate('create-supervised-user-link',
768                                              'create-link');
769       createLink.hidden = true;
770       links.appendChild(createLink);
772       var status = this.makeFromTemplate('status-container', 'status');
773       buttons.appendChild(status);
775       linkElement = createLink.querySelector('.signin-link');
776       linkElement.addEventListener('click',
777           this.createLinkPressed_.bind(this));
779       buttons.appendChild(this.makeButton(
780           'start',
781           'supervisedUserCreationFlow',
782           this.startButtonPressed_.bind(this),
783           ['intro'],
784           ['custom-appearance', 'button-fancy', 'button-blue']));
786       buttons.appendChild(this.makeButton(
787           'prev',
788           'supervisedUserCreationFlow',
789           this.prevButtonPressed_.bind(this),
790           ['manager'],
791           []));
793       buttons.appendChild(this.makeButton(
794           'next',
795           'supervisedUserCreationFlow',
796           this.nextButtonPressed_.bind(this),
797           ['manager', 'username'],
798           []));
800       buttons.appendChild(this.makeButton(
801           'import',
802           'supervisedUserCreationFlow',
803           this.importButtonPressed_.bind(this),
804           ['import', 'import-password'],
805           []));
807       buttons.appendChild(this.makeButton(
808           'gotit',
809           'supervisedUserCreationFlow',
810           this.gotItButtonPressed_.bind(this),
811           ['created'],
812           ['custom-appearance', 'button-fancy', 'button-blue']));
813       return [links, buttons];
814     },
816     /**
817      * Does sanity check and calls backend with current user name/password pair
818      * to authenticate manager. May result in showManagerPasswordError.
819      * @private
820      */
821     validateAndLogInAsManager_: function() {
822       var selectedPod = this.managerList_.selectedPod_;
823       if (null == selectedPod)
824         return;
826       var managerId = selectedPod.user.username;
827       var managerDisplayId = selectedPod.user.emailAddress;
828       var managerPassword = selectedPod.passwordElement.value;
829       if (managerPassword.length == 0)
830         return;
831       if (this.disabled)
832         return;
833       this.disabled = true;
834       this.context_.managerId = managerId;
835       this.context_.managerDisplayId = managerDisplayId;
836       this.context_.managerName = selectedPod.user.displayName;
837       chrome.send('authenticateManagerInSupervisedUserCreationFlow',
838           [managerId, managerPassword]);
839     },
841     /**
842      * Does sanity check and calls backend with user display name/password pair
843      * to create a user.
844      * @private
845      */
846     validateAndCreateSupervisedUser_: function() {
847       var firstPassword = $('supervised-user-creation-password').value;
848       var secondPassword =
849           $('supervised-user-creation-password-confirm').value;
850       var userName = $('supervised-user-creation-name').value;
851       if (firstPassword != secondPassword) {
852         this.showPasswordError(loadTimeData.getString(
853             'createSupervisedUserPasswordMismatchError'));
854         return;
855       }
856       if (this.disabled)
857         return;
858       this.disabled = true;
860       this.context_.supervisedName = userName;
861       chrome.send('specifySupervisedUserCreationFlowUserData',
862           [userName, firstPassword]);
863     },
865     /**
866      * Does sanity check and calls backend with selected existing supervised
867      * user id to import user.
868      * @private
869      */
870     importSupervisedUser_: function() {
871       if (this.disabled)
872         return;
873       if (this.currentPage_ == 'import-password') {
874         var firstPassword = this.getScreenElement('password').value;
875         var secondPassword = this.getScreenElement('password-confirm').value;
876         if (firstPassword != secondPassword) {
877           this.showPasswordError(loadTimeData.getString(
878               'createSupervisedUserPasswordMismatchError'));
879           return;
880         }
881         var userId = this.context_.importUserId;
882         this.disabled = true;
883         chrome.send('importSupervisedUserWithPassword',
884             [userId, firstPassword]);
885         return;
886       } else {
887         var selectedPod = this.importList_.selectedPod_;
888         if (!selectedPod)
889           return;
890         var user = selectedPod.user;
891         var userId = user.id;
893         this.context_.importUserId = userId;
894         this.context_.supervisedName = user.name;
895         this.context_.selectedImageUrl = user.avatarurl;
896         if (!user.needPassword) {
897           this.disabled = true;
898           chrome.send('importSupervisedUser', [userId]);
899         } else {
900           this.setVisiblePage_('import-password');
901         }
902       }
903     },
905     /**
906      * Calls backend part to check if current user name is valid/not taken.
907      * Results in a call to either supervisedUserNameOk or
908      * supervisedUserNameError.
909      * @private
910      */
911     checkUserName_: function() {
912       var userName = this.getScreenElement('name').value;
914       // Avoid flickering
915       if (userName == this.lastIncorrectUserName_ ||
916           userName == this.lastVerifiedName_) {
917         return;
918       }
919       if (userName.length > 0) {
920         chrome.send('checkSupervisedUserName', [userName]);
921       } else {
922         this.nameErrorVisible = false;
923         this.lastVerifiedName_ = null;
924         this.lastIncorrectUserName_ = null;
925         this.updateNextButtonForUser_();
926       }
927     },
929     /**
930      * Called by backend part in case of successful name validation.
931      * @param {string} name - name that was validated.
932      */
933     supervisedUserNameOk: function(name) {
934       this.lastVerifiedName_ = name;
935       this.lastIncorrectUserName_ = null;
936       if ($('supervised-user-creation-name').value == name)
937         this.clearUserNameError_();
938       this.updateNextButtonForUser_();
939     },
941     /**
942      * Called by backend part in case of name validation failure.
943      * @param {string} name - name that was validated.
944      * @param {string} errorText - reason why this name is invalid.
945      */
946     supervisedUserNameError: function(name, errorText) {
947       this.disabled = false;
948       this.lastIncorrectUserName_ = name;
949       this.lastVerifiedName_ = null;
951       var userNameField = $('supervised-user-creation-name');
952       if (userNameField.value == this.lastIncorrectUserName_) {
953         this.nameErrorVisible = true;
954         $('bubble').showTextForElement(
955             $('supervised-user-creation-name'),
956             errorText,
957             cr.ui.Bubble.Attachment.RIGHT,
958             12, 4);
959         this.setButtonDisabledStatus('next', true);
960       }
961     },
963     supervisedUserSuggestImport: function(name, user_id) {
964       this.disabled = false;
965       this.lastIncorrectUserName_ = name;
966       this.lastVerifiedName_ = null;
968       var userNameField = $('supervised-user-creation-name');
969       var creationScreen = this;
971       if (userNameField.value == this.lastIncorrectUserName_) {
972         this.nameErrorVisible = true;
973         var link = this.ownerDocument.createElement('div');
974         link.innerHTML = loadTimeData.getStringF(
975             'importBubbleText',
976             '<a class="signin-link" href="#">',
977             name,
978             '</a>');
979         link.querySelector('.signin-link').addEventListener('click',
980             function(e) {
981               creationScreen.handleSuggestImport_(user_id);
982               e.stopPropagation();
983             });
984         $('bubble').showContentForElement(
985             $('supervised-user-creation-name'),
986             cr.ui.Bubble.Attachment.RIGHT,
987             link,
988             12, 4);
989         this.setButtonDisabledStatus('next', true);
990       }
991     },
993     /**
994      * Clears user name error, if name is no more guaranteed to be invalid.
995      * @private
996      */
997     clearUserNameError_: function() {
998       // Avoid flickering
999       if ($('supervised-user-creation-name').value ==
1000               this.lastIncorrectUserName_) {
1001         return;
1002       }
1003       this.nameErrorVisible = false;
1004     },
1006     /**
1007      * Called by backend part in case of password validation failure.
1008      * @param {string} errorText - reason why this password is invalid.
1009      */
1010     showPasswordError: function(errorText) {
1011       $('bubble').showTextForElement(
1012           $('supervised-user-creation-password'),
1013           errorText,
1014           cr.ui.Bubble.Attachment.RIGHT,
1015           12, 4);
1016       $('supervised-user-creation-password').classList.add('password-error');
1017       $('supervised-user-creation-password').focus();
1018       this.disabled = false;
1019       this.setButtonDisabledStatus('next', true);
1020     },
1022     /**
1023      * True if user name error should be displayed.
1024      * @type {boolean}
1025      */
1026     set nameErrorVisible(value) {
1027       $('supervised-user-creation-name').
1028           classList.toggle('duplicate-name', value);
1029       if (!value)
1030         $('bubble').hide();
1031     },
1033     /**
1034      * Updates state of Continue button after minimal checks.
1035      * @return {boolean} true, if form seems to be valid.
1036      * @private
1037      */
1038     updateNextButtonForManager_: function() {
1039       var selectedPod = this.managerList_.selectedPod_;
1040       canProceed = null != selectedPod &&
1041                    selectedPod.passwordElement.value.length > 0;
1043       this.setButtonDisabledStatus('next', !canProceed);
1044       return canProceed;
1045     },
1047     /**
1048      * Updates state of Continue button after minimal checks.
1049      * @return {boolean} true, if form seems to be valid.
1050      * @private
1051      */
1052     updateNextButtonForUser_: function() {
1053       var firstPassword = this.getScreenElement('password').value;
1054       var secondPassword = this.getScreenElement('password-confirm').value;
1055       var userName = this.getScreenElement('name').value;
1057       var passwordOk = (firstPassword.length > 0) &&
1058           (firstPassword.length == secondPassword.length);
1060       if (this.currentPage_ == 'import-password') {
1061         this.setButtonDisabledStatus('import', !passwordOk);
1062         return passwordOk;
1063       }
1064       var imageGrid = this.getScreenElement('image-grid');
1065       var imageChosen = !(imageGrid.selectionType == 'camera' &&
1066                           imageGrid.cameraLive);
1067       var canProceed =
1068           passwordOk &&
1069           (userName.length > 0) &&
1070           this.lastVerifiedName_ &&
1071           (userName == this.lastVerifiedName_) &&
1072           imageChosen;
1074       this.setButtonDisabledStatus('next', !canProceed);
1075       return canProceed;
1076     },
1078     showSelectedManagerPasswordError_: function() {
1079       var selectedPod = this.managerList_.selectedPod_;
1080       selectedPod.showPasswordError();
1081       selectedPod.passwordElement.value = '';
1082       selectedPod.focusInput();
1083       this.updateNextButtonForManager_();
1084     },
1086     /**
1087      * Enables one particular subpage and hides the rest.
1088      * @param {string} visiblePage - name of subpage.
1089      * @private
1090      */
1091     setVisiblePage_: function(visiblePage) {
1092       this.disabled = false;
1093       this.updateText_();
1094       $('bubble').hide();
1095       if (!this.imagesRequested_) {
1096         chrome.send('supervisedUserGetImages');
1097         this.imagesRequested_ = true;
1098       }
1099       var pageNames = ['intro',
1100                        'manager',
1101                        'username',
1102                        'import',
1103                        'error',
1104                        'created'];
1105       var pageButtons = {'intro' : 'start',
1106                          'error' : 'error',
1107                          'import' : 'import',
1108                          'import-password' : 'import',
1109                          'created' : 'gotit'};
1110       this.hideStatus_();
1111       var pageToDisplay = visiblePage;
1112       if (visiblePage == 'import-password')
1113         pageToDisplay = 'username';
1115       for (i in pageNames) {
1116         var pageName = pageNames[i];
1117         var page = $('supervised-user-creation-' + pageName);
1118         page.hidden = (pageName != pageToDisplay);
1119         if (pageName == pageToDisplay)
1120           $('step-logo').hidden = page.classList.contains('step-no-logo');
1121       }
1123       for (i in this.buttonIds) {
1124         var button = this.getScreenButton(this.buttonIds[i]);
1125         button.hidden = button.pages.indexOf(visiblePage) < 0;
1126         button.disabled = false;
1127       }
1129       var pagesWithCancel = ['intro', 'manager', 'username', 'import-password',
1130           'error', 'import'];
1131       $('login-header-bar').allowCancel =
1132           pagesWithCancel.indexOf(visiblePage) > -1;
1133       $('cancel-add-user-button').disabled = false;
1135       this.getScreenElement('import-link').hidden = true;
1136       this.getScreenElement('create-link').hidden = true;
1138       if (pageButtons[visiblePage])
1139         this.getScreenButton(pageButtons[visiblePage]).focus();
1141       this.currentPage_ = visiblePage;
1143       if (visiblePage == 'manager' || visiblePage == 'intro') {
1144         $('supervised-user-creation-password').classList.remove(
1145             'password-error');
1146         if (this.managerList_.pods.length > 0)
1147           this.managerList_.selectPod(this.managerList_.pods[0]);
1148         $('login-header-bar').updateUI_();
1149       }
1151       if (visiblePage == 'username' || visiblePage == 'import-password') {
1152         var elements = this.getScreenElement(pageToDisplay).
1153             querySelectorAll('.hide-on-import');
1154         for (var i = 0; i < elements.length; i++) {
1155           elements[i].classList.toggle('hidden-on-import',
1156               visiblePage == 'import-password');
1157         }
1158       }
1159       if (visiblePage == 'username') {
1160         var imageGrid = this.getScreenElement('image-grid');
1161         // select some image.
1162         var selected = this.imagesData_[
1163             Math.floor(Math.random() * this.imagesData_.length)];
1164         this.context_.selectedImageUrl = selected.url;
1165         imageGrid.selectedItemUrl = selected.url;
1166         chrome.send('supervisedUserSelectImage',
1167                     [selected.url, 'default']);
1168         this.getScreenElement('image-grid').redraw();
1169         this.checkUserName_();
1170         this.updateNextButtonForUser_();
1171         this.getScreenElement('name').focus();
1172         this.getScreenElement('import-link').hidden =
1173             this.importList_.pods.length == 0;
1174       } else if (visiblePage == 'import-password') {
1175         var imageGrid = this.getScreenElement('image-grid');
1176         var selected;
1177         if ('selectedImageUrl' in this.context_) {
1178           selected = this.context_.selectedImageUrl;
1179         } else {
1180           // select some image.
1181           selected = this.imagesData_[
1182               Math.floor(Math.random() * this.imagesData_.length)].url;
1183           chrome.send('supervisedUserSelectImage',
1184                       [selected, 'default']);
1185         }
1186         imageGrid.selectedItemUrl = selected;
1187         this.getScreenElement('image-grid').redraw();
1189         this.updateNextButtonForUser_();
1191         this.getScreenElement('password').focus();
1192         this.getScreenElement('import-link').hidden = true;
1193       } else {
1194         this.getScreenElement('image-grid').stopCamera();
1195       }
1196       if (visiblePage == 'import') {
1197         this.getScreenElement('create-link').hidden = false;
1198         this.getScreenButton('import').disabled =
1199             !this.importList_.selectedPod_ ||
1200             this.importList_.selectedPod_.user.exists;
1201       }
1202       $('supervised-user-creation-close-button-item').hidden =
1203           (visiblePage == 'created');
1205       chrome.send('currentSupervisedUserPage', [this.currentPage_]);
1206     },
1208     setButtonDisabledStatus: function(buttonName, status) {
1209       var button = $('supervised-user-creation-' + buttonName + '-button');
1210       button.disabled = status;
1211     },
1213     gotItButtonPressed_: function() {
1214       chrome.send('finishLocalSupervisedUserCreation');
1215     },
1217     handleErrorButtonPressed_: function() {
1218       chrome.send('abortLocalSupervisedUserCreation');
1219     },
1221     startButtonPressed_: function() {
1222       this.setVisiblePage_('manager');
1223       this.setButtonDisabledStatus('next', true);
1224     },
1226     nextButtonPressed_: function() {
1227       if (this.currentPage_ == 'manager') {
1228         this.validateAndLogInAsManager_();
1229         return;
1230       }
1231       if (this.currentPage_ == 'username') {
1232         this.validateAndCreateSupervisedUser_();
1233       }
1234     },
1236     importButtonPressed_: function() {
1237       this.importSupervisedUser_();
1238     },
1240     importLinkPressed_: function() {
1241       this.setVisiblePage_('import');
1242     },
1244     handleSuggestImport_: function(user_id) {
1245       this.setVisiblePage_('import');
1246       this.importList_.selectUser(user_id);
1247     },
1249     createLinkPressed_: function() {
1250       this.setVisiblePage_('username');
1251       this.lastIncorrectUserName_ = null;
1252       this.lastVerifiedName_ = null;
1253       this.checkUserName_();
1254     },
1256     prevButtonPressed_: function() {
1257       this.setVisiblePage_('intro');
1258     },
1260     showProgress: function(text) {
1261       var status = this.getScreenElement('status');
1262       var statusText = status.querySelector('.id-text');
1263       statusText.textContent = text;
1264       statusText.classList.remove('error');
1265       status.querySelector('.id-spinner').hidden = false;
1266       status.hidden = false;
1267       this.getScreenElement('import-link').hidden = true;
1268       this.getScreenElement('create-link').hidden = true;
1269     },
1271     showStatusError: function(text) {
1272       var status = this.getScreenElement('status');
1273       var statusText = status.querySelector('.id-text');
1274       statusText.textContent = text;
1275       statusText.classList.add('error');
1276       status.querySelector('.id-spinner').hidden = true;
1277       status.hidden = false;
1278       this.getScreenElement('import-link').hidden = true;
1279       this.getScreenElement('create-link').hidden = true;
1280     },
1282     hideStatus_: function() {
1283       var status = this.getScreenElement('status');
1284       status.hidden = true;
1285     },
1287     /**
1288      * Updates state of login header so that necessary buttons are displayed.
1289      */
1290     onBeforeShow: function(data) {
1291       $('login-header-bar').signinUIState =
1292           SIGNIN_UI_STATE.SUPERVISED_USER_CREATION_FLOW;
1293       if (data['managers']) {
1294         this.loadManagers(data['managers']);
1295       }
1296       var imageGrid = this.getScreenElement('image-grid');
1297       imageGrid.updateAndFocus();
1298     },
1300     /**
1301      * Update state of login header so that necessary buttons are displayed.
1302      */
1303     onBeforeHide: function() {
1304       $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
1305       this.getScreenElement('image-grid').stopCamera();
1306     },
1308     /**
1309      * Returns a control which should receive an initial focus.
1310      */
1311     get defaultControl() {
1312       return $('supervised-user-creation-name');
1313     },
1315     /**
1316      * True if the the screen is disabled (handles no user interaction).
1317      * @type {boolean}
1318      */
1319     disabled_: false,
1321     get disabled() {
1322       return this.disabled_;
1323     },
1325     set disabled(value) {
1326       this.disabled_ = value;
1327       var controls = this.querySelectorAll('button,input');
1328       for (var i = 0, control; control = controls[i]; ++i) {
1329         control.disabled = value;
1330       }
1331       $('login-header-bar').disabled = value;
1332       $('cancel-add-user-button').disabled = false;
1333     },
1335     /**
1336      * Called by backend part to propagate list of possible managers.
1337      * @param {Array} userList - list of users that can be managers.
1338      */
1339     loadManagers: function(userList) {
1340       $('supervised-user-creation-managers-block').hidden = false;
1341       this.managerList_.clearPods();
1342       for (var i = 0; i < userList.length; ++i)
1343         this.managerList_.addPod(userList[i]);
1344       if (userList.length > 0)
1345         this.managerList_.selectPod(this.managerList_.pods[0]);
1346     },
1348     /**
1349      * Cancels user creation and drops to user screen (either sign).
1350      */
1351     cancel: function() {
1352       var notSignedInPages = ['intro', 'manager'];
1353       var postCreationPages = ['created'];
1354       if (notSignedInPages.indexOf(this.currentPage_) >= 0) {
1355         chrome.send('hideLocalSupervisedUserCreation');
1357         // Make sure no manager password is kept:
1358         this.managerList_.clearPods();
1360         Oobe.showUserPods();
1361         return;
1362       }
1363       if (postCreationPages.indexOf(this.currentPage_) >= 0) {
1364         chrome.send('finishLocalSupervisedUserCreation');
1365         return;
1366       }
1367       chrome.send('abortLocalSupervisedUserCreation');
1368     },
1370     updateText_: function() {
1371       var managerDisplayId = this.context_.managerDisplayId;
1372       this.updateElementText_('intro-alternate-text',
1373                               'createSupervisedUserIntroAlternateText');
1374       this.updateElementText_('created-text-1',
1375                               'createSupervisedUserCreatedText1',
1376                               this.context_.supervisedName);
1377       // TODO(antrim): Move wrapping with strong in grd file, and eliminate this
1378       //call.
1379       this.updateElementText_('created-text-2',
1380                               'createSupervisedUserCreatedText2',
1381                               this.wrapStrong(
1382                                   loadTimeData.getString('managementURL')),
1383                                   this.context_.supervisedName);
1384       this.updateElementText_('created-text-3',
1385                               'createSupervisedUserCreatedText3',
1386                               managerDisplayId);
1387       this.updateElementText_('name-explanation',
1388                               'createSupervisedUserNameExplanation',
1389                               managerDisplayId);
1390     },
1392     wrapStrong: function(original) {
1393       if (original == undefined)
1394         return original;
1395       return '<strong>' + original + '</strong>';
1396     },
1398     updateElementText_: function(localId, templateName) {
1399       var args = Array.prototype.slice.call(arguments);
1400       args.shift();
1401       this.getScreenElement(localId).innerHTML =
1402           loadTimeData.getStringF.apply(loadTimeData, args);
1403     },
1405     showIntroPage: function() {
1406       $('supervised-user-creation-password').value = '';
1407       $('supervised-user-creation-password-confirm').value = '';
1408       $('supervised-user-creation-name').value = '';
1410       this.lastVerifiedName_ = null;
1411       this.lastIncorrectUserName_ = null;
1412       this.passwordErrorVisible = false;
1413       $('supervised-user-creation-password').classList.remove('password-error');
1414       this.nameErrorVisible = false;
1416       this.setVisiblePage_('intro');
1417     },
1419     showManagerPage: function() {
1420       this.setVisiblePage_('manager');
1421     },
1423     showUsernamePage: function() {
1424       this.setVisiblePage_('username');
1425     },
1427     showTutorialPage: function() {
1428       this.setVisiblePage_('created');
1429     },
1431     showPage: function(page) {
1432       this.setVisiblePage_(page);
1433     },
1435     showErrorPage: function(errorTitle, errorText, errorButtonText) {
1436       this.disabled = false;
1437       $('supervised-user-creation-error-title').innerHTML = errorTitle;
1438       $('supervised-user-creation-error-text').innerHTML = errorText;
1439       $('supervised-user-creation-error-button').textContent = errorButtonText;
1440       this.setVisiblePage_('error');
1441     },
1443     showManagerPasswordError: function() {
1444       this.disabled = false;
1445       this.showSelectedManagerPasswordError_();
1446     },
1448     /*
1449     TODO(antrim) : this is an explicit code duplications with UserImageScreen.
1450     It should be removed by issue 251179.
1451     */
1452     /**
1453      * Currently selected user image index (take photo button is with zero
1454      * index).
1455      * @type {number}
1456      */
1457     selectedUserImage_: -1,
1458     imagesData: [],
1460     setDefaultImages: function(imagesData) {
1461       var imageGrid = this.getScreenElement('image-grid');
1462       imageGrid.setDefaultImages(imagesData);
1463       this.imagesData_ = imagesData;
1464     },
1467     handleActivate_: function() {
1468       var imageGrid = this.getScreenElement('image-grid');
1469       if (imageGrid.selectedItemUrl == ButtonImages.TAKE_PHOTO) {
1470         this.handleTakePhoto_();
1471         return;
1472       }
1473       this.nextButtonPressed_();
1474     },
1476     /**
1477      * Handles selection change.
1478      * @param {Event} e Selection change event.
1479      * @private
1480      */
1481     handleSelect_: function(e) {
1482       var imageGrid = this.getScreenElement('image-grid');
1483       this.updateNextButtonForUser_();
1485       $('supervised-user-creation-flip-photo').tabIndex =
1486           (imageGrid.selectionType == 'camera') ? 0 : -1;
1487       if (imageGrid.cameraLive || imageGrid.selectionType != 'camera')
1488         imageGrid.previewElement.classList.remove('phototaken');
1489       else
1490         imageGrid.previewElement.classList.add('phototaken');
1492       if (!imageGrid.cameraLive || imageGrid.selectionType != 'camera') {
1493         this.context_.selectedImageUrl = imageGrid.selectedItemUrl;
1494         chrome.send('supervisedUserSelectImage',
1495                     [imageGrid.selectedItemUrl, imageGrid.selectionType]);
1496       }
1497       // Start/stop camera on (de)selection.
1498       if (!imageGrid.inProgramSelection &&
1499           imageGrid.selectionType != e.oldSelectionType) {
1500         if (imageGrid.selectionType == 'camera') {
1501           // Programmatic selection of camera item is done in
1502           // startCamera callback where streaming is started by itself.
1503           imageGrid.startCamera(
1504               function() {
1505                 // Start capture if camera is still the selected item.
1506                 $('supervised-user-creation-image-preview-img').classList.
1507                     toggle('animated-transform', true);
1508                 return imageGrid.selectedItem == imageGrid.cameraImage;
1509               });
1510         } else {
1511           $('supervised-user-creation-image-preview-img').classList.toggle(
1512               'animated-transform', false);
1513           imageGrid.stopCamera();
1514         }
1515       }
1516     },
1518     /**
1519      * Handle camera-photo flip.
1520      */
1521     handleFlipPhoto_: function() {
1522       var imageGrid = this.getScreenElement('image-grid');
1523       imageGrid.previewElement.classList.add('animation');
1524       imageGrid.flipPhoto = !imageGrid.flipPhoto;
1525       var flipMessageId = imageGrid.flipPhoto ?
1526          'photoFlippedAccessibleText' : 'photoFlippedBackAccessibleText';
1527       announceAccessibleMessage(loadTimeData.getString(flipMessageId));
1528     },
1530     /**
1531      * Handle photo capture from the live camera stream.
1532      */
1533     handleTakePhoto_: function(e) {
1534       this.getScreenElement('image-grid').takePhoto();
1535       chrome.send('supervisedUserTakePhoto');
1536     },
1538     handlePhotoTaken_: function(e) {
1539       chrome.send('supervisedUserPhotoTaken', [e.dataURL]);
1540       announceAccessibleMessage(
1541           loadTimeData.getString('photoCaptureAccessibleText'));
1542     },
1544     /**
1545      * Handle photo updated event.
1546      * @param {Event} e Event with 'dataURL' property containing a data URL.
1547      */
1548     handlePhotoUpdated_: function(e) {
1549       chrome.send('supervisedUserPhotoTaken', [e.dataURL]);
1550     },
1552     /**
1553      * Handle discarding the captured photo.
1554      */
1555     handleDiscardPhoto_: function(e) {
1556       var imageGrid = this.getScreenElement('image-grid');
1557       imageGrid.discardPhoto();
1558       chrome.send('supervisedUserDiscardPhoto');
1559       announceAccessibleMessage(
1560           loadTimeData.getString('photoDiscardAccessibleText'));
1561     },
1563     setCameraPresent: function(present) {
1564       this.getScreenElement('image-grid').cameraPresent = present;
1565     },
1567     setExistingSupervisedUsers: function(users) {
1568       var selectedUser = null;
1569       // Store selected user
1570       if (this.importList_.selectedPod)
1571         selectedUser = this.importList_.selectedPod.user.id;
1573       var userList = users;
1574       userList.sort(function(a, b) {
1575         // Put existing users last.
1576         if (a.exists != b.exists)
1577           return a.exists ? 1 : -1;
1578         // Sort rest by name.
1579         return a.name.localeCompare(b.name, [], {sensitivity: 'base'});
1580       });
1582       this.importList_.clearPods();
1583       var selectedIndex = -1;
1584       for (var i = 0; i < userList.length; ++i) {
1585         this.importList_.addPod(userList[i]);
1586         if (selectedUser == userList[i].id)
1587           selectedIndex = i;
1588       }
1590       if (userList.length == 1)
1591         this.importList_.selectPod(this.importList_.pods[0]);
1593       if (selectedIndex >= 0)
1594         this.importList_.selectPod(this.importList_.pods[selectedIndex]);
1596       if (this.currentPage_ == 'username')
1597         this.getScreenElement('import-link').hidden = (userList.length == 0);
1598     },
1599   };