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