Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / chromeos / login / screen_supervised_user_creation.js
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.
4
5 /**
6  * @fileoverview Supervised user creation flow screen.
7  */
8
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;
14
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   });
21
22   ManagerPod.userImageSalt_ = {};
23
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,
31
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       };
45
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);
54
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     },
68
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];
75
76       this.nameElement.textContent = this.user.displayName;
77       this.emailElement.textContent = this.user.emailAddress;
78     },
79
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     },
89
90     /**
91      * Brings focus to password field.
92      */
93     focusInput: function() {
94       this.passwordElement.focus();
95     },
96
97     /**
98      * Gets image element.
99      * @type {!HTMLImageElement}
100      */
101     get imageElement() {
102       return this.querySelector('.supervised-user-creation-manager-image');
103     },
104
105     /**
106      * Gets name element.
107      * @type {!HTMLDivElement}
108      */
109     get nameElement() {
110       return this.querySelector('.supervised-user-creation-manager-name');
111     },
112
113     /**
114      * Gets e-mail element.
115      * @type {!HTMLDivElement}
116      */
117     get emailElement() {
118       return this.querySelector('.supervised-user-creation-manager-email');
119     },
120
121     /**
122      * Gets password element.
123      * @type {!HTMLDivElement}
124      */
125     get passwordElement() {
126       return this.querySelector('.supervised-user-creation-manager-password');
127     },
128
129     /**
130      * Gets password enclosing block.
131      * @type {!HTMLDivElement}
132      */
133     get passwordBlock() {
134       return this.querySelector('.password-block');
135     },
136
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     },
143
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   };
157
158   var ManagerPodList = cr.ui.define('div');
159
160   /**
161    * UI element for selecting manager account for new supervised user.
162    * @type {Object}
163    */
164   ManagerPodList.prototype = {
165     __proto__: HTMLDivElement.prototype,
166
167     selectedPod_: null,
168
169     /** @override */
170     decorate: function() {
171     },
172
173     /**
174      * Returns all the pods in this pod list.
175      * @type {NodeList}
176      */
177     get pods() {
178       return this.children;
179     },
180
181     addPod: function(manager) {
182       var managerPod = new ManagerPod({user: manager});
183       this.appendChild(managerPod);
184       managerPod.update();
185     },
186
187     clearPods: function() {
188       this.innerHTML = '';
189       this.selectedPod_ = null;
190     },
191
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     },
214
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   };
240
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   });
247
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,
255
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     },
264
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     },
282
283     /**
284      * Gets image element.
285      * @type {!HTMLImageElement}
286      */
287     get imageElement() {
288       return this.querySelector('.import-pod-image');
289     },
290
291     /**
292      * Gets name element.
293      * @type {!HTMLDivElement}
294      */
295     get nameElement() {
296       return this.querySelector('.import-pod-name');
297     },
298
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     },
305
306     /**
307      * The user that this pod represents.
308      * @type {Object}
309      */
310     user_: undefined,
311
312     get user() {
313       return this.user_;
314     },
315
316     set user(userDict) {
317       this.user_ = userDict;
318       this.update();
319     },
320   };
321
322   var ImportPodList = cr.ui.define('div');
323
324   /**
325    * UI element for selecting existing supervised user for import.
326    * @type {Object}
327    */
328   ImportPodList.prototype = {
329     __proto__: HTMLDivElement.prototype,
330
331     selectedPod_: null,
332
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');
339
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       });
346
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     },
365
366     /**
367      * Returns all the pods in this pod list.
368      * @type {NodeList}
369      */
370     get pods() {
371       return this.children;
372     },
373
374     /**
375      * Returns selected pod.
376      * @type {Node}
377      */
378     get selectedPod() {
379       return this.selectedPod_;
380     },
381
382     addPod: function(user) {
383       var importPod = new ImportPod({user: user});
384       this.appendChild(importPod);
385       importPod.update();
386     },
387
388     clearPods: function() {
389       this.innerHTML = '';
390       this.selectedPod_ = null;
391     },
392
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;
399
400       var self = scroller;
401
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);
412
413         if (top + itemHeight > scrollTop + clientHeight - paddingY) {
414           self.scrollTop = top + itemHeight - clientHeight + paddingY;
415           return true;
416         }
417         return false;
418       };
419
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     },
434
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     },
464
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     },
483
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   };
494
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     ],
515
516     lastVerifiedName_: null,
517     lastIncorrectUserName_: null,
518     managerList_: null,
519     importList_: null,
520
521     currentPage_: null,
522     imagesRequested_: false,
523
524     // Contains data that can be auto-shared with handler.
525     context_: {},
526
527     /** @override */
528     decorate: function() {
529       this.managerList_ = new ManagerPodList();
530       $('supervised-user-creation-managers-pane').appendChild(
531           this.managerList_);
532
533       this.importList_ = new ImportPodList();
534       $('supervised-user-creation-import-pane').appendChild(this.importList_);
535
536       var userNameField = $('supervised-user-creation-name');
537       var passwordField = $('supervised-user-creation-password');
538       var password2Field = $('supervised-user-creation-password-confirm');
539
540       var creationScreen = this;
541
542       var hideUserPasswordError = function(element) {
543         $('bubble').hide();
544         $('supervised-user-creation-password').classList.remove(
545             'password-error');
546       };
547
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);
569
570       this.getScreenButton('error').addEventListener('click', function(e) {
571         creationScreen.handleErrorButtonPressed_();
572         e.stopPropagation();
573       });
574
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);
581
582       // Preview image will track the selected item's URL.
583       var previewElement = this.getScreenElement('image-preview');
584       previewElement.oncontextmenu = function(e) { e.preventDefault(); };
585
586       imageGrid.previewElement = previewElement;
587       imageGrid.selectionType = 'default';
588       imageGrid.flipPhotoElement = this.getScreenElement('flip-photo');
589
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'));
602
603       this.getScreenElement('take-photo').addEventListener(
604           'click', this.handleTakePhoto_.bind(this));
605       this.getScreenElement('discard-photo').addEventListener(
606           'click', this.handleDiscardPhoto_.bind(this));
607
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           });
619     },
620
621     buttonIds: [],
622
623     /**
624      * Creates button for adding to controls.
625      * @param {string} buttonId -- id for button, have to be unique within
626      *   screen. Actual id will be prefixed with screen name and appended with
627      *   '-button'. Use getScreenButton(buttonId) to find it later.
628      * @param {string} i18nPrefix -- screen prefix for i18n values.
629      * @param {function} callback -- will be called on button press with
630      *   buttonId parameter.
631      * @param {array} pages -- list of pages where this button should be
632      *   displayed.
633      * @param {array} classes -- list of additional CSS classes for button.
634      */
635     makeButton: function(buttonId, i18nPrefix, callback, pages, classes) {
636       var capitalizedId = buttonId.charAt(0).toUpperCase() + buttonId.slice(1);
637       this.buttonIds.push(buttonId);
638       var result = this.ownerDocument.createElement('button');
639       result.id = this.name() + '-' + buttonId + '-button';
640       result.classList.add('screen-control-button');
641       for (var i = 0; i < classes.length; i++) {
642         result.classList.add(classes[i]);
643       }
644       result.textContent = loadTimeData.
645           getString(i18nPrefix + capitalizedId + 'ButtonTitle');
646       result.addEventListener('click', function(e) {
647         callback(buttonId);
648         e.stopPropagation();
649       });
650       result.pages = pages;
651       return result;
652     },
653
654     /**
655      * Simple validator for |configureTextInput|.
656      * Element is considered valid if it has any text.
657      * @param {Element} element - element to be validated.
658      * @return {boolean} - true, if element has any text.
659      */
660     validIfNotEmpty_: function(element) {
661       return (element.value.length > 0);
662     },
663
664     /**
665      * Configure text-input |element|.
666      * @param {Element} element - element to be configured.
667      * @param {function(element)} inputChangeListener - function that will be
668      *    called upon any button press/release.
669      * @param {function(element)} validator - function that will be called when
670      *    Enter is pressed. If it returns |true| then advance to next element.
671      * @param {function(element)} moveFocus - function that will determine next
672      *    element and move focus to it.
673      * @param {function(element)} errorHider - function that is called upon
674      *    every button press, so that any associated error can be hidden.
675      */
676     configureTextInput: function(element,
677                                  inputChangeListener,
678                                  validator,
679                                  moveFocus,
680                                  errorHider) {
681       element.addEventListener('keydown', function(e) {
682         if (e.keyIdentifier == 'Enter') {
683           var dataValid = true;
684           if (validator)
685             dataValid = validator(element);
686           if (!dataValid) {
687             element.focus();
688           } else {
689             if (moveFocus)
690               moveFocus(element);
691           }
692           e.stopPropagation();
693           return;
694         }
695         if (errorHider)
696           errorHider(element);
697         if (inputChangeListener)
698           inputChangeListener(element);
699       });
700       element.addEventListener('keyup', function(e) {
701         if (inputChangeListener)
702           inputChangeListener(element);
703       });
704     },
705
706     /**
707      * Makes element from template.
708      * @param {string} templateId -- template will be looked up within screen
709      * by class with name "template-<templateId>".
710      * @param {string} elementId -- id for result, uinque within screen. Actual
711      *   id will be prefixed with screen name. Use getScreenElement(id) to find
712      *   it later.
713      */
714     makeFromTemplate: function(templateId, elementId) {
715       var templateClassName = 'template-' + templateId;
716       var templateNode = this.querySelector('.' + templateClassName);
717       var screenPrefix = this.name() + '-';
718       var result = templateNode.cloneNode(true);
719       result.classList.remove(templateClassName);
720       result.id = screenPrefix + elementId;
721       return result;
722     },
723
724     /**
725      * @param {string} buttonId -- id of button to be found,
726      * @return {Element} button created by makeButton with given buttonId.
727      */
728     getScreenButton: function(buttonId) {
729       var fullId = this.name() + '-' + buttonId + '-button';
730       return this.getScreenElement(buttonId + '-button');
731     },
732
733     /**
734      * @param {string} elementId -- id of element to be found,
735      * @return {Element} button created by makeFromTemplate with elementId.
736      */
737     getScreenElement: function(elementId) {
738       var fullId = this.name() + '-' + elementId;
739       return $(fullId);
740     },
741
742     /**
743      * Screen controls.
744      * @type {!Array} Array of Buttons.
745      */
746     get buttons() {
747       var links = this.ownerDocument.createElement('div');
748       var buttons = this.ownerDocument.createElement('div');
749       links.classList.add('controls-links');
750       buttons.classList.add('controls-buttons');
751
752       var importLink = this.makeFromTemplate('import-supervised-user-link',
753                                              'import-link');
754       importLink.hidden = true;
755       links.appendChild(importLink);
756
757       var linkElement = importLink.querySelector('.signin-link');
758       linkElement.addEventListener('click',
759           this.importLinkPressed_.bind(this));
760
761       var createLink = this.makeFromTemplate('create-supervised-user-link',
762                                              'create-link');
763       createLink.hidden = true;
764       links.appendChild(createLink);
765
766       var status = this.makeFromTemplate('status-container', 'status');
767       buttons.appendChild(status);
768
769       linkElement = createLink.querySelector('.signin-link');
770       linkElement.addEventListener('click',
771           this.createLinkPressed_.bind(this));
772
773       buttons.appendChild(this.makeButton(
774           'start',
775           'supervisedUserCreationFlow',
776           this.startButtonPressed_.bind(this),
777           ['intro'],
778           ['custom-appearance', 'button-fancy', 'button-blue']));
779
780       buttons.appendChild(this.makeButton(
781           'prev',
782           'supervisedUserCreationFlow',
783           this.prevButtonPressed_.bind(this),
784           ['manager'],
785           []));
786
787       buttons.appendChild(this.makeButton(
788           'next',
789           'supervisedUserCreationFlow',
790           this.nextButtonPressed_.bind(this),
791           ['manager', 'username'],
792           []));
793
794       buttons.appendChild(this.makeButton(
795           'import',
796           'supervisedUserCreationFlow',
797           this.importButtonPressed_.bind(this),
798           ['import', 'import-password'],
799           []));
800
801       buttons.appendChild(this.makeButton(
802           'gotit',
803           'supervisedUserCreationFlow',
804           this.gotItButtonPressed_.bind(this),
805           ['created'],
806           ['custom-appearance', 'button-fancy', 'button-blue']));
807       return [links, buttons];
808     },
809
810     /**
811      * Does sanity check and calls backend with current user name/password pair
812      * to authenticate manager. May result in showManagerPasswordError.
813      * @private
814      */
815     validateAndLogInAsManager_: function() {
816       var selectedPod = this.managerList_.selectedPod_;
817       if (null == selectedPod)
818         return;
819
820       var managerId = selectedPod.user.username;
821       var managerDisplayId = selectedPod.user.emailAddress;
822       var managerPassword = selectedPod.passwordElement.value;
823       if (managerPassword.length == 0)
824         return;
825       if (this.disabled)
826         return;
827       this.disabled = true;
828       this.context_.managerId = managerId;
829       this.context_.managerDisplayId = managerDisplayId;
830       this.context_.managerName = selectedPod.user.displayName;
831       chrome.send('authenticateManagerInSupervisedUserCreationFlow',
832           [managerId, managerPassword]);
833     },
834
835     /**
836      * Does sanity check and calls backend with user display name/password pair
837      * to create a user.
838      * @private
839      */
840     validateAndCreateSupervisedUser_: function() {
841       var firstPassword = $('supervised-user-creation-password').value;
842       var secondPassword =
843           $('supervised-user-creation-password-confirm').value;
844       var userName = $('supervised-user-creation-name').value;
845       if (firstPassword != secondPassword) {
846         this.showPasswordError(loadTimeData.getString(
847             'createSupervisedUserPasswordMismatchError'));
848         return;
849       }
850       if (this.disabled)
851         return;
852       this.disabled = true;
853
854       this.context_.supervisedName = userName;
855       chrome.send('specifySupervisedUserCreationFlowUserData',
856           [userName, firstPassword]);
857     },
858
859     /**
860      * Does sanity check and calls backend with selected existing supervised
861      * user id to import user.
862      * @private
863      */
864     importSupervisedUser_: function() {
865       if (this.disabled)
866         return;
867       if (this.currentPage_ == 'import-password') {
868         var firstPassword = this.getScreenElement('password').value;
869         var secondPassword = this.getScreenElement('password-confirm').value;
870         if (firstPassword != secondPassword) {
871           this.showPasswordError(loadTimeData.getString(
872               'createSupervisedUserPasswordMismatchError'));
873           return;
874         }
875         var userId = this.context_.importUserId;
876         this.disabled = true;
877         chrome.send('importSupervisedUserWithPassword',
878             [userId, firstPassword]);
879         return;
880       } else {
881         var selectedPod = this.importList_.selectedPod_;
882         if (!selectedPod)
883           return;
884         var user = selectedPod.user;
885         var userId = user.id;
886
887         this.context_.importUserId = userId;
888         this.context_.supervisedName = user.name;
889         this.context_.selectedImageUrl = user.avatarurl;
890         if (!user.needPassword) {
891           this.disabled = true;
892           chrome.send('importSupervisedUser', [userId]);
893         } else {
894           this.setVisiblePage_('import-password');
895         }
896       }
897     },
898
899     /**
900      * Calls backend part to check if current user name is valid/not taken.
901      * Results in a call to either supervisedUserNameOk or
902      * supervisedUserNameError.
903      * @private
904      */
905     checkUserName_: function() {
906       var userName = this.getScreenElement('name').value;
907
908       // Avoid flickering
909       if (userName == this.lastIncorrectUserName_ ||
910           userName == this.lastVerifiedName_) {
911         return;
912       }
913       if (userName.length > 0) {
914         chrome.send('checkSupervisedUserName', [userName]);
915       } else {
916         this.nameErrorVisible = false;
917         this.lastVerifiedName_ = null;
918         this.lastIncorrectUserName_ = null;
919         this.updateNextButtonForUser_();
920       }
921     },
922
923     /**
924      * Called by backend part in case of successful name validation.
925      * @param {string} name - name that was validated.
926      */
927     supervisedUserNameOk: function(name) {
928       this.lastVerifiedName_ = name;
929       this.lastIncorrectUserName_ = null;
930       if ($('supervised-user-creation-name').value == name)
931         this.clearUserNameError_();
932       this.updateNextButtonForUser_();
933     },
934
935     /**
936      * Called by backend part in case of name validation failure.
937      * @param {string} name - name that was validated.
938      * @param {string} errorText - reason why this name is invalid.
939      */
940     supervisedUserNameError: function(name, errorText) {
941       this.disabled = false;
942       this.lastIncorrectUserName_ = name;
943       this.lastVerifiedName_ = null;
944
945       var userNameField = $('supervised-user-creation-name');
946       if (userNameField.value == this.lastIncorrectUserName_) {
947         this.nameErrorVisible = true;
948         $('bubble').showTextForElement(
949             $('supervised-user-creation-name'),
950             errorText,
951             cr.ui.Bubble.Attachment.RIGHT,
952             12, 4);
953         this.setButtonDisabledStatus('next', true);
954       }
955     },
956
957     supervisedUserSuggestImport: function(name, user_id) {
958       this.disabled = false;
959       this.lastIncorrectUserName_ = name;
960       this.lastVerifiedName_ = null;
961
962       var userNameField = $('supervised-user-creation-name');
963       var creationScreen = this;
964
965       if (userNameField.value == this.lastIncorrectUserName_) {
966         this.nameErrorVisible = true;
967         var link = this.ownerDocument.createElement('div');
968         link.innerHTML = loadTimeData.getStringF(
969             'importBubbleText',
970             '<a class="signin-link" href="#">',
971             name,
972             '</a>');
973         link.querySelector('.signin-link').addEventListener('click',
974             function(e) {
975               creationScreen.handleSuggestImport_(user_id);
976               e.stopPropagation();
977             });
978         $('bubble').showContentForElement(
979             $('supervised-user-creation-name'),
980             cr.ui.Bubble.Attachment.RIGHT,
981             link,
982             12, 4);
983         this.setButtonDisabledStatus('next', true);
984       }
985     },
986
987     /**
988      * Clears user name error, if name is no more guaranteed to be invalid.
989      * @private
990      */
991     clearUserNameError_: function() {
992       // Avoid flickering
993       if ($('supervised-user-creation-name').value ==
994               this.lastIncorrectUserName_) {
995         return;
996       }
997       this.nameErrorVisible = false;
998     },
999
1000     /**
1001      * Called by backend part in case of password validation failure.
1002      * @param {string} errorText - reason why this password is invalid.
1003      */
1004     showPasswordError: function(errorText) {
1005       $('bubble').showTextForElement(
1006           $('supervised-user-creation-password'),
1007           errorText,
1008           cr.ui.Bubble.Attachment.RIGHT,
1009           12, 4);
1010       $('supervised-user-creation-password').classList.add('password-error');
1011       $('supervised-user-creation-password').focus();
1012       this.disabled = false;
1013       this.setButtonDisabledStatus('next', true);
1014     },
1015
1016     /**
1017      * True if user name error should be displayed.
1018      * @type {boolean}
1019      */
1020     set nameErrorVisible(value) {
1021       $('supervised-user-creation-name').
1022           classList.toggle('duplicate-name', value);
1023       if (!value)
1024         $('bubble').hide();
1025     },
1026
1027     /**
1028      * Updates state of Continue button after minimal checks.
1029      * @return {boolean} true, if form seems to be valid.
1030      * @private
1031      */
1032     updateNextButtonForManager_: function() {
1033       var selectedPod = this.managerList_.selectedPod_;
1034       canProceed = null != selectedPod &&
1035                    selectedPod.passwordElement.value.length > 0;
1036
1037       this.setButtonDisabledStatus('next', !canProceed);
1038       return canProceed;
1039     },
1040
1041     /**
1042      * Updates state of Continue button after minimal checks.
1043      * @return {boolean} true, if form seems to be valid.
1044      * @private
1045      */
1046     updateNextButtonForUser_: function() {
1047       var firstPassword = this.getScreenElement('password').value;
1048       var secondPassword = this.getScreenElement('password-confirm').value;
1049       var userName = this.getScreenElement('name').value;
1050
1051       var passwordOk = (firstPassword.length > 0) &&
1052           (firstPassword.length == secondPassword.length);
1053
1054       if (this.currentPage_ == 'import-password') {
1055         this.setButtonDisabledStatus('import', !passwordOk);
1056         return passwordOk;
1057       }
1058       var imageGrid = this.getScreenElement('image-grid');
1059       var imageChosen = !(imageGrid.selectionType == 'camera' &&
1060                           imageGrid.cameraLive);
1061       var canProceed =
1062           passwordOk &&
1063           (userName.length > 0) &&
1064           this.lastVerifiedName_ &&
1065           (userName == this.lastVerifiedName_) &&
1066           imageChosen;
1067
1068       this.setButtonDisabledStatus('next', !canProceed);
1069       return canProceed;
1070     },
1071
1072     showSelectedManagerPasswordError_: function() {
1073       var selectedPod = this.managerList_.selectedPod_;
1074       selectedPod.showPasswordError();
1075       selectedPod.passwordElement.value = '';
1076       selectedPod.focusInput();
1077       this.updateNextButtonForManager_();
1078     },
1079
1080     /**
1081      * Enables one particular subpage and hides the rest.
1082      * @param {string} visiblePage - name of subpage.
1083      * @private
1084      */
1085     setVisiblePage_: function(visiblePage) {
1086       this.disabled = false;
1087       this.updateText_();
1088       $('bubble').hide();
1089       if (!this.imagesRequested_) {
1090         chrome.send('supervisedUserGetImages');
1091         this.imagesRequested_ = true;
1092       }
1093       var pageNames = ['intro',
1094                        'manager',
1095                        'username',
1096                        'import',
1097                        'error',
1098                        'created'];
1099       var pageButtons = {'intro' : 'start',
1100                          'error' : 'error',
1101                          'import' : 'import',
1102                          'import-password' : 'import',
1103                          'created' : 'gotit'};
1104       this.hideStatus_();
1105       var pageToDisplay = visiblePage;
1106       if (visiblePage == 'import-password')
1107         pageToDisplay = 'username';
1108
1109       for (i in pageNames) {
1110         var pageName = pageNames[i];
1111         var page = $('supervised-user-creation-' + pageName);
1112         page.hidden = (pageName != pageToDisplay);
1113         if (pageName == pageToDisplay)
1114           $('step-logo').hidden = page.classList.contains('step-no-logo');
1115       }
1116
1117       for (i in this.buttonIds) {
1118         var button = this.getScreenButton(this.buttonIds[i]);
1119         button.hidden = button.pages.indexOf(visiblePage) < 0;
1120         button.disabled = false;
1121       }
1122
1123       var pagesWithCancel = ['intro', 'manager', 'username', 'import-password',
1124           'error', 'import'];
1125       $('login-header-bar').allowCancel =
1126           pagesWithCancel.indexOf(visiblePage) > 0;
1127       $('cancel-add-user-button').disabled = false;
1128
1129       this.getScreenElement('import-link').hidden = true;
1130       this.getScreenElement('create-link').hidden = true;
1131
1132       if (pageButtons[visiblePage])
1133         this.getScreenButton(pageButtons[visiblePage]).focus();
1134
1135       this.currentPage_ = visiblePage;
1136
1137       if (visiblePage == 'manager' || visiblePage == 'intro') {
1138         $('supervised-user-creation-password').classList.remove(
1139             'password-error');
1140         if (this.managerList_.pods.length > 0)
1141           this.managerList_.selectPod(this.managerList_.pods[0]);
1142       }
1143
1144       if (visiblePage == 'username' || visiblePage == 'import-password') {
1145         var elements = this.getScreenElement(pageToDisplay).
1146             querySelectorAll('.hide-on-import');
1147         for (var i = 0; i < elements.length; i++) {
1148           elements[i].classList.toggle('hidden-on-import',
1149               visiblePage == 'import-password');
1150         }
1151       }
1152       if (visiblePage == 'username') {
1153         var imageGrid = this.getScreenElement('image-grid');
1154         // select some image.
1155         var selected = this.imagesData_[
1156             Math.floor(Math.random() * this.imagesData_.length)];
1157         this.context_.selectedImageUrl = selected.url;
1158         imageGrid.selectedItemUrl = selected.url;
1159         chrome.send('supervisedUserSelectImage',
1160                     [selected.url, 'default']);
1161         this.getScreenElement('image-grid').redraw();
1162         this.checkUserName_();
1163         this.updateNextButtonForUser_();
1164         this.getScreenElement('name').focus();
1165         this.getScreenElement('import-link').hidden =
1166             this.importList_.pods.length == 0;
1167       } else if (visiblePage == 'import-password') {
1168         var imageGrid = this.getScreenElement('image-grid');
1169         var selected;
1170         if ('selectedImageUrl' in this.context_) {
1171           selected = this.context_.selectedImageUrl;
1172         } else {
1173           // select some image.
1174           selected = this.imagesData_[
1175               Math.floor(Math.random() * this.imagesData_.length)].url;
1176           chrome.send('supervisedUserSelectImage',
1177                       [selected, 'default']);
1178         }
1179         imageGrid.selectedItemUrl = selected;
1180         this.getScreenElement('image-grid').redraw();
1181
1182         this.updateNextButtonForUser_();
1183
1184         this.getScreenElement('password').focus();
1185         this.getScreenElement('import-link').hidden = true;
1186       } else {
1187         this.getScreenElement('image-grid').stopCamera();
1188       }
1189       if (visiblePage == 'import') {
1190         this.getScreenElement('create-link').hidden = false;
1191         this.getScreenButton('import').disabled =
1192             !this.importList_.selectedPod_ ||
1193             this.importList_.selectedPod_.user.exists;
1194       }
1195       chrome.send('currentSupervisedUserPage', [this.currentPage_]);
1196     },
1197
1198     setButtonDisabledStatus: function(buttonName, status) {
1199       var button = $('supervised-user-creation-' + buttonName + '-button');
1200       button.disabled = status;
1201     },
1202
1203     gotItButtonPressed_: function() {
1204       chrome.send('finishLocalSupervisedUserCreation');
1205     },
1206
1207     handleErrorButtonPressed_: function() {
1208       chrome.send('abortLocalSupervisedUserCreation');
1209     },
1210
1211     startButtonPressed_: function() {
1212       this.setVisiblePage_('manager');
1213       this.setButtonDisabledStatus('next', true);
1214     },
1215
1216     nextButtonPressed_: function() {
1217       if (this.currentPage_ == 'manager') {
1218         this.validateAndLogInAsManager_();
1219         return;
1220       }
1221       if (this.currentPage_ == 'username') {
1222         this.validateAndCreateSupervisedUser_();
1223       }
1224     },
1225
1226     importButtonPressed_: function() {
1227       this.importSupervisedUser_();
1228     },
1229
1230     importLinkPressed_: function() {
1231       this.setVisiblePage_('import');
1232     },
1233
1234     handleSuggestImport_: function(user_id) {
1235       this.setVisiblePage_('import');
1236       this.importList_.selectUser(user_id);
1237     },
1238
1239     createLinkPressed_: function() {
1240       this.setVisiblePage_('username');
1241       this.lastIncorrectUserName_ = null;
1242       this.lastVerifiedName_ = null;
1243       this.checkUserName_();
1244     },
1245
1246     prevButtonPressed_: function() {
1247       this.setVisiblePage_('intro');
1248     },
1249
1250     showProgress: function(text) {
1251       var status = this.getScreenElement('status');
1252       var statusText = status.querySelector('.id-text');
1253       statusText.textContent = text;
1254       statusText.classList.remove('error');
1255       status.querySelector('.id-spinner').hidden = false;
1256       status.hidden = false;
1257       this.getScreenElement('import-link').hidden = true;
1258       this.getScreenElement('create-link').hidden = true;
1259     },
1260
1261     showStatusError: function(text) {
1262       var status = this.getScreenElement('status');
1263       var statusText = status.querySelector('.id-text');
1264       statusText.textContent = text;
1265       statusText.classList.add('error');
1266       status.querySelector('.id-spinner').hidden = true;
1267       status.hidden = false;
1268       this.getScreenElement('import-link').hidden = true;
1269       this.getScreenElement('create-link').hidden = true;
1270     },
1271
1272     hideStatus_: function() {
1273       var status = this.getScreenElement('status');
1274       status.hidden = true;
1275     },
1276
1277     /**
1278      * Updates state of login header so that necessary buttons are displayed.
1279      **/
1280     onBeforeShow: function(data) {
1281       $('login-header-bar').signinUIState =
1282           SIGNIN_UI_STATE.SUPERVISED_USER_CREATION_FLOW;
1283       if (data['managers']) {
1284         this.loadManagers(data['managers']);
1285       }
1286       var imageGrid = this.getScreenElement('image-grid');
1287       imageGrid.updateAndFocus();
1288     },
1289
1290     /**
1291      * Update state of login header so that necessary buttons are displayed.
1292      */
1293     onBeforeHide: function() {
1294       $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
1295       this.getScreenElement('image-grid').stopCamera();
1296     },
1297
1298     /**
1299      * Returns a control which should receive an initial focus.
1300      */
1301     get defaultControl() {
1302       return $('supervised-user-creation-name');
1303     },
1304
1305     /**
1306      * True if the the screen is disabled (handles no user interaction).
1307      * @type {boolean}
1308      */
1309     disabled_: false,
1310
1311     get disabled() {
1312       return this.disabled_;
1313     },
1314
1315     set disabled(value) {
1316       this.disabled_ = value;
1317       var controls = this.querySelectorAll('button,input');
1318       for (var i = 0, control; control = controls[i]; ++i) {
1319         control.disabled = value;
1320       }
1321       $('login-header-bar').disabled = value;
1322       $('cancel-add-user-button').disabled = false;
1323     },
1324
1325     /**
1326      * Called by backend part to propagate list of possible managers.
1327      * @param {Array} userList - list of users that can be managers.
1328      */
1329     loadManagers: function(userList) {
1330       $('supervised-user-creation-managers-block').hidden = false;
1331       this.managerList_.clearPods();
1332       for (var i = 0; i < userList.length; ++i)
1333         this.managerList_.addPod(userList[i]);
1334       if (userList.length > 0)
1335         this.managerList_.selectPod(this.managerList_.pods[0]);
1336     },
1337
1338     /**
1339      * Cancels user creation and drops to user screen (either sign).
1340      */
1341     cancel: function() {
1342       var notSignedInPages = ['intro', 'manager'];
1343       var postCreationPages = ['created'];
1344       if (notSignedInPages.indexOf(this.currentPage_) >= 0) {
1345         // Make sure no manager password is kept:
1346         this.managerList_.clearPods();
1347
1348         $('pod-row').loadLastWallpaper();
1349
1350         Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
1351         Oobe.resetSigninUI(true);
1352         return;
1353       }
1354       if (postCreationPages.indexOf(this.currentPage_) >= 0) {
1355         chrome.send('finishLocalSupervisedUserCreation');
1356         return;
1357       }
1358       chrome.send('abortLocalSupervisedUserCreation');
1359     },
1360
1361     updateText_: function() {
1362       var managerDisplayId = this.context_.managerDisplayId;
1363       this.updateElementText_('intro-alternate-text',
1364                               'createSupervisedUserIntroAlternateText');
1365       this.updateElementText_('created-text-1',
1366                               'createSupervisedUserCreatedText1',
1367                               this.context_.supervisedName);
1368       // TODO(antrim): Move wrapping with strong in grd file, and eliminate this
1369       //call.
1370       this.updateElementText_('created-text-2',
1371                               'createSupervisedUserCreatedText2',
1372                               this.wrapStrong(
1373                                   loadTimeData.getString('managementURL')),
1374                                   this.context_.supervisedName);
1375       this.updateElementText_('created-text-3',
1376                               'createSupervisedUserCreatedText3',
1377                               managerDisplayId);
1378       this.updateElementText_('name-explanation',
1379                               'createSupervisedUserNameExplanation',
1380                               managerDisplayId);
1381     },
1382
1383     wrapStrong: function(original) {
1384       if (original == undefined)
1385         return original;
1386       return '<strong>' + original + '</strong>';
1387     },
1388
1389     updateElementText_: function(localId, templateName) {
1390       var args = Array.prototype.slice.call(arguments);
1391       args.shift();
1392       this.getScreenElement(localId).innerHTML =
1393           loadTimeData.getStringF.apply(loadTimeData, args);
1394     },
1395
1396     showIntroPage: function() {
1397       $('supervised-user-creation-password').value = '';
1398       $('supervised-user-creation-password-confirm').value = '';
1399       $('supervised-user-creation-name').value = '';
1400
1401       this.lastVerifiedName_ = null;
1402       this.lastIncorrectUserName_ = null;
1403       this.passwordErrorVisible = false;
1404       $('supervised-user-creation-password').classList.remove('password-error');
1405       this.nameErrorVisible = false;
1406
1407       this.setVisiblePage_('intro');
1408     },
1409
1410     showManagerPage: function() {
1411       this.setVisiblePage_('manager');
1412     },
1413
1414     showUsernamePage: function() {
1415       this.setVisiblePage_('username');
1416     },
1417
1418     showTutorialPage: function() {
1419       this.setVisiblePage_('created');
1420     },
1421
1422     showPage: function(page) {
1423       this.setVisiblePage_(page);
1424     },
1425
1426     showErrorPage: function(errorTitle, errorText, errorButtonText) {
1427       this.disabled = false;
1428       $('supervised-user-creation-error-title').innerHTML = errorTitle;
1429       $('supervised-user-creation-error-text').innerHTML = errorText;
1430       $('supervised-user-creation-error-button').textContent = errorButtonText;
1431       this.setVisiblePage_('error');
1432     },
1433
1434     showManagerPasswordError: function() {
1435       this.disabled = false;
1436       this.showSelectedManagerPasswordError_();
1437     },
1438
1439     /*
1440     TODO(antrim) : this is an explicit code duplications with UserImageScreen.
1441     It should be removed by issue 251179.
1442     */
1443     /**
1444      * Currently selected user image index (take photo button is with zero
1445      * index).
1446      * @type {number}
1447      */
1448     selectedUserImage_: -1,
1449     imagesData: [],
1450
1451     setDefaultImages: function(imagesData) {
1452       var imageGrid = this.getScreenElement('image-grid');
1453       for (var i = 0, data; data = imagesData[i]; i++) {
1454         var item = imageGrid.addItem(data.url, data.title);
1455         item.type = 'default';
1456         item.author = data.author || '';
1457         item.website = data.website || '';
1458       }
1459       this.imagesData_ = imagesData;
1460     },
1461
1462
1463     handleActivate_: function() {
1464       var imageGrid = this.getScreenElement('image-grid');
1465       if (imageGrid.selectedItemUrl == ButtonImages.TAKE_PHOTO) {
1466         this.handleTakePhoto_();
1467         return;
1468       }
1469       this.nextButtonPressed_();
1470     },
1471
1472     /**
1473      * Handles selection change.
1474      * @param {Event} e Selection change event.
1475      * @private
1476      */
1477     handleSelect_: function(e) {
1478       var imageGrid = this.getScreenElement('image-grid');
1479       this.updateNextButtonForUser_();
1480
1481       $('supervised-user-creation-flip-photo').tabIndex =
1482           (imageGrid.selectionType == 'camera') ? 0 : -1;
1483       if (imageGrid.cameraLive || imageGrid.selectionType != 'camera')
1484         imageGrid.previewElement.classList.remove('phototaken');
1485       else
1486         imageGrid.previewElement.classList.add('phototaken');
1487
1488       if (!imageGrid.cameraLive || imageGrid.selectionType != 'camera') {
1489         this.context_.selectedImageUrl = imageGrid.selectedItemUrl;
1490         chrome.send('supervisedUserSelectImage',
1491                     [imageGrid.selectedItemUrl, imageGrid.selectionType]);
1492       }
1493       // Start/stop camera on (de)selection.
1494       if (!imageGrid.inProgramSelection &&
1495           imageGrid.selectionType != e.oldSelectionType) {
1496         if (imageGrid.selectionType == 'camera') {
1497           // Programmatic selection of camera item is done in
1498           // startCamera callback where streaming is started by itself.
1499           imageGrid.startCamera(
1500               function() {
1501                 // Start capture if camera is still the selected item.
1502                 $('supervised-user-creation-image-preview-img').classList.
1503                     toggle('animated-transform', true);
1504                 return imageGrid.selectedItem == imageGrid.cameraImage;
1505               });
1506         } else {
1507           $('supervised-user-creation-image-preview-img').classList.toggle(
1508               'animated-transform', false);
1509           imageGrid.stopCamera();
1510         }
1511       }
1512     },
1513
1514     /**
1515      * Handle camera-photo flip.
1516      */
1517     handleFlipPhoto_: function() {
1518       var imageGrid = this.getScreenElement('image-grid');
1519       imageGrid.previewElement.classList.add('animation');
1520       imageGrid.flipPhoto = !imageGrid.flipPhoto;
1521       var flipMessageId = imageGrid.flipPhoto ?
1522          'photoFlippedAccessibleText' : 'photoFlippedBackAccessibleText';
1523       announceAccessibleMessage(loadTimeData.getString(flipMessageId));
1524     },
1525
1526     /**
1527      * Handle photo capture from the live camera stream.
1528      */
1529     handleTakePhoto_: function(e) {
1530       this.getScreenElement('image-grid').takePhoto();
1531       chrome.send('supervisedUserTakePhoto');
1532     },
1533
1534     handlePhotoTaken_: function(e) {
1535       chrome.send('supervisedUserPhotoTaken', [e.dataURL]);
1536       announceAccessibleMessage(
1537           loadTimeData.getString('photoCaptureAccessibleText'));
1538     },
1539
1540     /**
1541      * Handle photo updated event.
1542      * @param {Event} e Event with 'dataURL' property containing a data URL.
1543      */
1544     handlePhotoUpdated_: function(e) {
1545       chrome.send('supervisedUserPhotoTaken', [e.dataURL]);
1546     },
1547
1548     /**
1549      * Handle discarding the captured photo.
1550      */
1551     handleDiscardPhoto_: function(e) {
1552       var imageGrid = this.getScreenElement('image-grid');
1553       imageGrid.discardPhoto();
1554       chrome.send('supervisedUserDiscardPhoto');
1555       announceAccessibleMessage(
1556           loadTimeData.getString('photoDiscardAccessibleText'));
1557     },
1558
1559     setCameraPresent: function(present) {
1560       this.getScreenElement('image-grid').cameraPresent = present;
1561     },
1562
1563     setExistingSupervisedUsers: function(users) {
1564       var selectedUser = null;
1565       // Store selected user
1566       if (this.importList_.selectedPod)
1567         selectedUser = this.importList_.selectedPod.user.id;
1568
1569       var userList = users;
1570       userList.sort(function(a, b) {
1571         // Put existing users last.
1572         if (a.exists != b.exists)
1573           return a.exists ? 1 : -1;
1574         // Sort rest by name.
1575         return a.name.localeCompare(b.name, [], {sensitivity: 'base'});
1576       });
1577
1578       this.importList_.clearPods();
1579       var selectedIndex = -1;
1580       for (var i = 0; i < userList.length; ++i) {
1581         this.importList_.addPod(userList[i]);
1582         if (selectedUser == userList[i].id)
1583           selectedIndex = i;
1584       }
1585
1586       if (userList.length == 1)
1587         this.importList_.selectPod(this.importList_.pods[0]);
1588
1589       if (selectedIndex >= 0)
1590         this.importList_.selectPod(this.importList_.pods[selectedIndex]);
1591
1592       if (this.currentPage_ == 'username')
1593         this.getScreenElement('import-link').hidden = (userList.length == 0);
1594     },
1595   };
1596 });
1597