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.
6 * @fileoverview Legacy supervised user creation flow screen.
9 login
.createScreen('SupervisedUserCreationScreen',
10 'supervised-user-creation', function() {
11 var MAX_NAME_LENGTH
= 50;
12 var UserImagesGrid
= options
.UserImagesGrid
;
13 var ButtonImages
= UserImagesGrid
.ButtonImages
;
15 var ManagerPod
= cr
.ui
.define(function() {
16 var node
= $('supervised-user-creation-manager-template').cloneNode(true);
17 node
.removeAttribute('id');
18 node
.removeAttribute('hidden');
22 ManagerPod
.userImageSalt_
= {};
25 * UI element for displaying single account in list of possible managers for
26 * new supervised user.
29 ManagerPod
.prototype = {
30 __proto__
: HTMLDivElement
.prototype,
33 decorate: function() {
34 // Mousedown has to be used instead of click to be able to prevent 'focus'
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');
46 screen
.configureTextInput(
48 screen
.updateNextButtonForManager_
.bind(screen
),
49 screen
.validIfNotEmpty_
.bind(screen
),
51 screen
.getScreenButton('next').focus();
53 hideManagerPasswordError
);
55 this.passwordElement
.addEventListener('keydown', function(e
) {
56 switch (e
.keyIdentifier
) {
58 managerPodList
.selectNextPod(-1);
62 managerPodList
.selectNextPod(+1);
70 * Updates UI elements from user data.
73 this.imageElement
.src
= 'chrome://userimage/' + this.user
.username
+
74 '?id=' + ManagerPod
.userImageSalt_
[this.user
.username
];
76 this.nameElement
.textContent
= this.user
.displayName
;
77 this.emailElement
.textContent
= this.user
.emailAddress
;
80 showPasswordError: function() {
81 this.passwordElement
.classList
.add('password-error');
82 $('bubble').showTextForElement(
84 loadTimeData
.getString(
85 'createSupervisedUserWrongManagerPasswordText'),
86 cr
.ui
.Bubble
.Attachment
.BOTTOM
,
91 * Brings focus to password field.
93 focusInput: function() {
94 this.passwordElement
.focus();
99 * @type {!HTMLImageElement}
102 return this.querySelector('.supervised-user-creation-manager-image');
107 * @type {!HTMLDivElement}
110 return this.querySelector('.supervised-user-creation-manager-name');
114 * Gets e-mail element.
115 * @type {!HTMLDivElement}
118 return this.querySelector('.supervised-user-creation-manager-email');
122 * Gets password element.
123 * @type {!HTMLDivElement}
125 get passwordElement() {
126 return this.querySelector('.supervised-user-creation-manager-password');
130 * Gets password enclosing block.
131 * @type {!HTMLDivElement}
133 get passwordBlock() {
134 return this.querySelector('.password-block');
138 handleMouseDown_: function(e
) {
139 this.parentNode
.selectPod(this);
140 // Prevent default so that we don't trigger 'focus' event.
145 * The user that this pod represents.
153 this.user_
= userDict
;
158 var ManagerPodList
= cr
.ui
.define('div');
161 * UI element for selecting manager account for new supervised user.
164 ManagerPodList
.prototype = {
165 __proto__
: HTMLDivElement
.prototype,
170 decorate: function() {
174 * Returns all the pods in this pod list.
178 return this.children
;
181 addPod: function(manager
) {
182 var managerPod
= new ManagerPod({user
: manager
});
183 this.appendChild(managerPod
);
187 clearPods: function() {
189 this.selectedPod_
= null;
192 selectPod: function(podToSelect
) {
193 if ((this.selectedPod_
== podToSelect
) && !!podToSelect
) {
194 podToSelect
.focusInput();
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;
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
]);
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.
221 selectNextPod: function(direction
) {
222 if (!this.selectedPod_
)
225 for (var i
= 0, pod
; pod
= this.pods
[i
]; ++i
) {
226 if (pod
== this.selectedPod_
) {
233 index
= index
+ direction
;
234 if (index
< 0 || index
>= this.pods
.length
)
236 this.selectPod(this.pods
[index
]);
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');
249 * UI element for displaying single supervised user in list of possible users
250 * for importing existing users.
253 ImportPod
.prototype = {
254 __proto__
: HTMLDivElement
.prototype,
257 decorate: function() {
258 // Mousedown has to be used instead of click to be able to prevent 'focus'
260 this.addEventListener('mousedown', this.handleMouseDown_
.bind(this));
261 var screen
= $('supervised-user-creation');
262 var importList
= screen
.importList_
;
266 * Updates UI elements from user data.
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
);
276 this.nameElement
.textContent
=
277 loadTimeData
.getStringF('importUsernameExists', this.user
.name
);
280 this.classList
.toggle('imported', this.user
.exists
);
284 * Gets image element.
285 * @type {!HTMLImageElement}
288 return this.querySelector('.import-pod-image');
293 * @type {!HTMLDivElement}
296 return this.querySelector('.import-pod-name');
300 handleMouseDown_: function(e
) {
301 this.parentNode
.selectPod(this);
302 // Prevent default so that we don't trigger 'focus' event.
307 * The user that this pod represents.
317 this.user_
= userDict
;
322 var ImportPodList
= cr
.ui
.define('div');
325 * UI element for selecting existing supervised user for import.
328 ImportPodList
.prototype = {
329 __proto__
: HTMLDivElement
.prototype,
334 decorate: function() {
335 this.setAttribute('tabIndex', 0);
336 this.classList
.add('nofocus');
337 var importList
= this;
338 var screen
= $('supervised-user-creation');
340 this.addEventListener('focus', function(e
) {
341 if (importList
.selectedPod_
== null) {
342 if (importList
.pods
.length
> 0)
343 importList
.selectPod(importList
.pods
[0]);
347 this.addEventListener('keydown', function(e
) {
348 switch (e
.keyIdentifier
) {
350 importList
.selectNextPod(-1);
354 if (importList
.selectedPod_
!= null)
355 screen
.importSupervisedUser_();
359 importList
.selectNextPod(+1);
367 * Returns all the pods in this pod list.
371 return this.children
;
375 * Returns selected pod.
379 return this.selectedPod_
;
382 addPod: function(user
) {
383 var importPod
= new ImportPod({user
: user
});
384 this.appendChild(importPod
);
388 clearPods: function() {
390 this.selectedPod_
= null;
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
;
402 // Function to adjust the tops of viewport and row.
403 function scrollToAdjustTop() {
404 self
.scrollTop
= top
;
407 // Function to adjust the bottoms of viewport and row.
408 function scrollToAdjustBottom() {
409 var cs
= getComputedStyle(self
);
410 var paddingY
= parseInt(cs
.paddingTop
, 10) +
411 parseInt(cs
.paddingBottom
, 10);
413 if (top
+ itemHeight
> scrollTop
+ clientHeight
- paddingY
) {
414 self
.scrollTop
= top
+ itemHeight
- clientHeight
+ paddingY
;
420 // Check if the entire of given indexed row can be shown in the viewport.
421 if (itemHeight
<= clientHeight
) {
423 return scrollToAdjustTop();
424 if (scrollTop
+ clientHeight
< top
+ itemHeight
)
425 return scrollToAdjustBottom();
428 return scrollToAdjustTop();
429 if (top
+ itemHeight
< scrollTop
+ clientHeight
)
430 return scrollToAdjustBottom();
436 * @param {Element} podToSelect - pod to select, can be null.
438 selectPod: function(podToSelect
) {
439 if ((this.selectedPod_
== podToSelect
) && !!podToSelect
) {
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');
450 podToSelect
.classList
.add('focused');
452 var screen
= $('supervised-user-creation');
453 if (!this.selectedPod_
) {
454 screen
.getScreenButton('import').disabled
= true;
456 screen
.getScreenButton('import').disabled
=
457 this.selectedPod_
.user
.exists
;
458 if (!this.selectedPod_
.user
.exists
) {
459 chrome
.send('userSelectedForImportInSupervisedUserCreationFlow',
460 [podToSelect
.user
.id
]);
465 selectNextPod: function(direction
) {
466 if (!this.selectedPod_
)
469 for (var i
= 0, pod
; pod
= this.pods
[i
]; ++i
) {
470 if (pod
== this.selectedPod_
) {
477 index
= index
+ direction
;
478 if (index
< 0 || index
>= this.pods
.length
)
480 this.selectPod(this.pods
[index
]);
484 selectUser: function(user_id
) {
485 for (var i
= 0, pod
; pod
= this.pods
[i
]; ++i
) {
486 if (pod
.user
.id
== user_id
) {
488 this.scrollIntoView(pod
);
500 'setExistingSupervisedUsers',
504 'showManagerPasswordError',
511 'supervisedUserNameError',
512 'supervisedUserNameOk',
513 'supervisedUserSuggestImport',
516 lastVerifiedName_
: null,
517 lastIncorrectUserName_
: null,
522 imagesRequested_
: false,
524 // Contains data that can be auto-shared with handler.
528 decorate: function() {
529 this.managerList_
= new ManagerPodList();
530 $('supervised-user-creation-managers-pane').appendChild(
533 this.importList_
= new ImportPodList();
534 $('supervised-user-creation-import-pane').appendChild(this.importList_
);
536 var userNameField
= $('supervised-user-creation-name');
537 var passwordField
= $('supervised-user-creation-password');
538 var password2Field
= $('supervised-user-creation-password-confirm');
540 var creationScreen
= this;
542 var hideUserPasswordError = function(element
) {
544 $('supervised-user-creation-password').classList
.remove(
548 this.configureTextInput(userNameField
,
549 this.checkUserName_
.bind(this),
550 this.validIfNotEmpty_
.bind(this),
552 passwordField
.focus();
554 this.clearUserNameError_
.bind(this));
555 this.configureTextInput(passwordField
,
556 this.updateNextButtonForUser_
.bind(this),
557 this.validIfNotEmpty_
.bind(this),
559 password2Field
.focus();
561 hideUserPasswordError
);
562 this.configureTextInput(password2Field
,
563 this.updateNextButtonForUser_
.bind(this),
564 this.validIfNotEmpty_
.bind(this),
566 creationScreen
.getScreenButton('next').focus();
568 hideUserPasswordError
);
570 this.getScreenButton('error').addEventListener('click', function(e
) {
571 creationScreen
.handleErrorButtonPressed_();
576 TODO(antrim) : this is an explicit code duplications with UserImageScreen.
577 It should be removed by issue 251179.
579 var imageGrid
= this.getScreenElement('image-grid');
580 UserImagesGrid
.decorate(imageGrid
);
582 // Preview image will track the selected item's URL.
583 var previewElement
= this.getScreenElement('image-preview');
584 previewElement
.oncontextmenu = function(e
) { e
.preventDefault(); };
586 imageGrid
.previewElement
= previewElement
;
587 imageGrid
.selectionType
= 'default';
588 imageGrid
.flipPhotoElement
= this.getScreenElement('flip-photo');
590 imageGrid
.addEventListener('activate',
591 this.handleActivate_
.bind(this));
592 imageGrid
.addEventListener('select',
593 this.handleSelect_
.bind(this));
594 imageGrid
.addEventListener('phototaken',
595 this.handlePhotoTaken_
.bind(this));
596 imageGrid
.addEventListener('photoupdated',
597 this.handlePhotoUpdated_
.bind(this));
598 // Set the title for camera item in the grid.
599 imageGrid
.setCameraTitles(
600 loadTimeData
.getString('takePhoto'),
601 loadTimeData
.getString('photoFromCamera'));
603 this.getScreenElement('take-photo').addEventListener(
604 'click', this.handleTakePhoto_
.bind(this));
605 this.getScreenElement('discard-photo').addEventListener(
606 'click', this.handleDiscardPhoto_
.bind(this));
608 // Toggle 'animation' class for the duration of WebKit transition.
609 this.getScreenElement('flip-photo').addEventListener(
610 'click', this.handleFlipPhoto_
.bind(this));
611 this.getScreenElement('image-stream-crop').addEventListener(
612 'webkitTransitionEnd', function(e
) {
613 previewElement
.classList
.remove('animation');
615 this.getScreenElement('image-preview-img').addEventListener(
616 'webkitTransitionEnd', function(e
) {
617 previewElement
.classList
.remove('animation');
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
633 * @param {array} classes -- list of additional CSS classes for button.
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
]);
644 result
.textContent
= loadTimeData
.
645 getString(i18nPrefix
+ capitalizedId
+ 'ButtonTitle');
646 result
.addEventListener('click', function(e
) {
650 result
.pages
= pages
;
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.
660 validIfNotEmpty_: function(element
) {
661 return (element
.value
.length
> 0);
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.
676 configureTextInput: function(element
,
681 element
.addEventListener('keydown', function(e
) {
682 if (e
.keyIdentifier
== 'Enter') {
683 var dataValid
= true;
685 dataValid
= validator(element
);
697 if (inputChangeListener
)
698 inputChangeListener(element
);
700 element
.addEventListener('keyup', function(e
) {
701 if (inputChangeListener
)
702 inputChangeListener(element
);
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
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
;
725 * @param {string} buttonId -- id of button to be found,
726 * @return {Element} button created by makeButton with given buttonId.
728 getScreenButton: function(buttonId
) {
729 var fullId
= this.name() + '-' + buttonId
+ '-button';
730 return this.getScreenElement(buttonId
+ '-button');
734 * @param {string} elementId -- id of element to be found,
735 * @return {Element} button created by makeFromTemplate with elementId.
737 getScreenElement: function(elementId
) {
738 var fullId
= this.name() + '-' + elementId
;
744 * @type {!Array} Array of 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');
752 var importLink
= this.makeFromTemplate('import-supervised-user-link',
754 importLink
.hidden
= true;
755 links
.appendChild(importLink
);
757 var linkElement
= importLink
.querySelector('.signin-link');
758 linkElement
.addEventListener('click',
759 this.importLinkPressed_
.bind(this));
761 var createLink
= this.makeFromTemplate('create-supervised-user-link',
763 createLink
.hidden
= true;
764 links
.appendChild(createLink
);
766 var status
= this.makeFromTemplate('status-container', 'status');
767 buttons
.appendChild(status
);
769 linkElement
= createLink
.querySelector('.signin-link');
770 linkElement
.addEventListener('click',
771 this.createLinkPressed_
.bind(this));
773 buttons
.appendChild(this.makeButton(
775 'supervisedUserCreationFlow',
776 this.startButtonPressed_
.bind(this),
778 ['custom-appearance', 'button-fancy', 'button-blue']));
780 buttons
.appendChild(this.makeButton(
782 'supervisedUserCreationFlow',
783 this.prevButtonPressed_
.bind(this),
787 buttons
.appendChild(this.makeButton(
789 'supervisedUserCreationFlow',
790 this.nextButtonPressed_
.bind(this),
791 ['manager', 'username'],
794 buttons
.appendChild(this.makeButton(
796 'supervisedUserCreationFlow',
797 this.importButtonPressed_
.bind(this),
798 ['import', 'import-password'],
801 buttons
.appendChild(this.makeButton(
803 'supervisedUserCreationFlow',
804 this.gotItButtonPressed_
.bind(this),
806 ['custom-appearance', 'button-fancy', 'button-blue']));
807 return [links
, buttons
];
811 * Does sanity check and calls backend with current user name/password pair
812 * to authenticate manager. May result in showManagerPasswordError.
815 validateAndLogInAsManager_: function() {
816 var selectedPod
= this.managerList_
.selectedPod_
;
817 if (null == selectedPod
)
820 var managerId
= selectedPod
.user
.username
;
821 var managerDisplayId
= selectedPod
.user
.emailAddress
;
822 var managerPassword
= selectedPod
.passwordElement
.value
;
823 if (managerPassword
.length
== 0)
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
]);
836 * Does sanity check and calls backend with user display name/password pair
840 validateAndCreateSupervisedUser_: function() {
841 var firstPassword
= $('supervised-user-creation-password').value
;
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'));
852 this.disabled
= true;
854 this.context_
.supervisedName
= userName
;
855 chrome
.send('specifySupervisedUserCreationFlowUserData',
856 [userName
, firstPassword
]);
860 * Does sanity check and calls backend with selected existing supervised
861 * user id to import user.
864 importSupervisedUser_: function() {
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'));
875 var userId
= this.context_
.importUserId
;
876 this.disabled
= true;
877 chrome
.send('importSupervisedUserWithPassword',
878 [userId
, firstPassword
]);
881 var selectedPod
= this.importList_
.selectedPod_
;
884 var user
= selectedPod
.user
;
885 var userId
= user
.id
;
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
]);
894 this.setVisiblePage_('import-password');
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.
905 checkUserName_: function() {
906 var userName
= this.getScreenElement('name').value
;
909 if (userName
== this.lastIncorrectUserName_
||
910 userName
== this.lastVerifiedName_
) {
913 if (userName
.length
> 0) {
914 chrome
.send('checkSupervisedUserName', [userName
]);
916 this.nameErrorVisible
= false;
917 this.lastVerifiedName_
= null;
918 this.lastIncorrectUserName_
= null;
919 this.updateNextButtonForUser_();
924 * Called by backend part in case of successful name validation.
925 * @param {string} name - name that was validated.
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_();
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.
940 supervisedUserNameError: function(name
, errorText
) {
941 this.disabled
= false;
942 this.lastIncorrectUserName_
= name
;
943 this.lastVerifiedName_
= null;
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'),
951 cr
.ui
.Bubble
.Attachment
.RIGHT
,
953 this.setButtonDisabledStatus('next', true);
957 supervisedUserSuggestImport: function(name
, user_id
) {
958 this.disabled
= false;
959 this.lastIncorrectUserName_
= name
;
960 this.lastVerifiedName_
= null;
962 var userNameField
= $('supervised-user-creation-name');
963 var creationScreen
= this;
965 if (userNameField
.value
== this.lastIncorrectUserName_
) {
966 this.nameErrorVisible
= true;
967 var link
= this.ownerDocument
.createElement('div');
968 link
.innerHTML
= loadTimeData
.getStringF(
970 '<a class="signin-link" href="#">',
973 link
.querySelector('.signin-link').addEventListener('click',
975 creationScreen
.handleSuggestImport_(user_id
);
978 $('bubble').showContentForElement(
979 $('supervised-user-creation-name'),
980 cr
.ui
.Bubble
.Attachment
.RIGHT
,
983 this.setButtonDisabledStatus('next', true);
988 * Clears user name error, if name is no more guaranteed to be invalid.
991 clearUserNameError_: function() {
993 if ($('supervised-user-creation-name').value
==
994 this.lastIncorrectUserName_
) {
997 this.nameErrorVisible
= false;
1001 * Called by backend part in case of password validation failure.
1002 * @param {string} errorText - reason why this password is invalid.
1004 showPasswordError: function(errorText
) {
1005 $('bubble').showTextForElement(
1006 $('supervised-user-creation-password'),
1008 cr
.ui
.Bubble
.Attachment
.RIGHT
,
1010 $('supervised-user-creation-password').classList
.add('password-error');
1011 $('supervised-user-creation-password').focus();
1012 this.disabled
= false;
1013 this.setButtonDisabledStatus('next', true);
1017 * True if user name error should be displayed.
1020 set nameErrorVisible(value
) {
1021 $('supervised-user-creation-name').
1022 classList
.toggle('duplicate-name', value
);
1028 * Updates state of Continue button after minimal checks.
1029 * @return {boolean} true, if form seems to be valid.
1032 updateNextButtonForManager_: function() {
1033 var selectedPod
= this.managerList_
.selectedPod_
;
1034 canProceed
= null != selectedPod
&&
1035 selectedPod
.passwordElement
.value
.length
> 0;
1037 this.setButtonDisabledStatus('next', !canProceed
);
1042 * Updates state of Continue button after minimal checks.
1043 * @return {boolean} true, if form seems to be valid.
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
;
1051 var passwordOk
= (firstPassword
.length
> 0) &&
1052 (firstPassword
.length
== secondPassword
.length
);
1054 if (this.currentPage_
== 'import-password') {
1055 this.setButtonDisabledStatus('import', !passwordOk
);
1058 var imageGrid
= this.getScreenElement('image-grid');
1059 var imageChosen
= !(imageGrid
.selectionType
== 'camera' &&
1060 imageGrid
.cameraLive
);
1063 (userName
.length
> 0) &&
1064 this.lastVerifiedName_
&&
1065 (userName
== this.lastVerifiedName_
) &&
1068 this.setButtonDisabledStatus('next', !canProceed
);
1072 showSelectedManagerPasswordError_: function() {
1073 var selectedPod
= this.managerList_
.selectedPod_
;
1074 selectedPod
.showPasswordError();
1075 selectedPod
.passwordElement
.value
= '';
1076 selectedPod
.focusInput();
1077 this.updateNextButtonForManager_();
1081 * Enables one particular subpage and hides the rest.
1082 * @param {string} visiblePage - name of subpage.
1085 setVisiblePage_: function(visiblePage
) {
1086 this.disabled
= false;
1089 if (!this.imagesRequested_
) {
1090 chrome
.send('supervisedUserGetImages');
1091 this.imagesRequested_
= true;
1093 var pageNames
= ['intro',
1099 var pageButtons
= {'intro' : 'start',
1101 'import' : 'import',
1102 'import-password' : 'import',
1103 'created' : 'gotit'};
1105 var pageToDisplay
= visiblePage
;
1106 if (visiblePage
== 'import-password')
1107 pageToDisplay
= 'username';
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');
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;
1123 var pagesWithCancel
= ['intro', 'manager', 'username', 'import-password',
1125 $('login-header-bar').allowCancel
=
1126 pagesWithCancel
.indexOf(visiblePage
) > 0;
1127 $('cancel-add-user-button').disabled
= false;
1129 this.getScreenElement('import-link').hidden
= true;
1130 this.getScreenElement('create-link').hidden
= true;
1132 if (pageButtons
[visiblePage
])
1133 this.getScreenButton(pageButtons
[visiblePage
]).focus();
1135 this.currentPage_
= visiblePage
;
1137 if (visiblePage
== 'manager' || visiblePage
== 'intro') {
1138 $('supervised-user-creation-password').classList
.remove(
1140 if (this.managerList_
.pods
.length
> 0)
1141 this.managerList_
.selectPod(this.managerList_
.pods
[0]);
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');
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');
1170 if ('selectedImageUrl' in this.context_
) {
1171 selected
= this.context_
.selectedImageUrl
;
1173 // select some image.
1174 selected
= this.imagesData_
[
1175 Math
.floor(Math
.random() * this.imagesData_
.length
)].url
;
1176 chrome
.send('supervisedUserSelectImage',
1177 [selected
, 'default']);
1179 imageGrid
.selectedItemUrl
= selected
;
1180 this.getScreenElement('image-grid').redraw();
1182 this.updateNextButtonForUser_();
1184 this.getScreenElement('password').focus();
1185 this.getScreenElement('import-link').hidden
= true;
1187 this.getScreenElement('image-grid').stopCamera();
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
;
1195 chrome
.send('currentSupervisedUserPage', [this.currentPage_
]);
1198 setButtonDisabledStatus: function(buttonName
, status
) {
1199 var button
= $('supervised-user-creation-' + buttonName
+ '-button');
1200 button
.disabled
= status
;
1203 gotItButtonPressed_: function() {
1204 chrome
.send('finishLocalSupervisedUserCreation');
1207 handleErrorButtonPressed_: function() {
1208 chrome
.send('abortLocalSupervisedUserCreation');
1211 startButtonPressed_: function() {
1212 this.setVisiblePage_('manager');
1213 this.setButtonDisabledStatus('next', true);
1216 nextButtonPressed_: function() {
1217 if (this.currentPage_
== 'manager') {
1218 this.validateAndLogInAsManager_();
1221 if (this.currentPage_
== 'username') {
1222 this.validateAndCreateSupervisedUser_();
1226 importButtonPressed_: function() {
1227 this.importSupervisedUser_();
1230 importLinkPressed_: function() {
1231 this.setVisiblePage_('import');
1234 handleSuggestImport_: function(user_id
) {
1235 this.setVisiblePage_('import');
1236 this.importList_
.selectUser(user_id
);
1239 createLinkPressed_: function() {
1240 this.setVisiblePage_('username');
1241 this.lastIncorrectUserName_
= null;
1242 this.lastVerifiedName_
= null;
1243 this.checkUserName_();
1246 prevButtonPressed_: function() {
1247 this.setVisiblePage_('intro');
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;
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;
1272 hideStatus_: function() {
1273 var status
= this.getScreenElement('status');
1274 status
.hidden
= true;
1278 * Updates state of login header so that necessary buttons are displayed.
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']);
1286 var imageGrid
= this.getScreenElement('image-grid');
1287 imageGrid
.updateAndFocus();
1291 * Update state of login header so that necessary buttons are displayed.
1293 onBeforeHide: function() {
1294 $('login-header-bar').signinUIState
= SIGNIN_UI_STATE
.HIDDEN
;
1295 this.getScreenElement('image-grid').stopCamera();
1299 * Returns a control which should receive an initial focus.
1301 get defaultControl() {
1302 return $('supervised-user-creation-name');
1306 * True if the the screen is disabled (handles no user interaction).
1312 return this.disabled_
;
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
;
1321 $('login-header-bar').disabled
= value
;
1322 $('cancel-add-user-button').disabled
= false;
1326 * Called by backend part to propagate list of possible managers.
1327 * @param {Array} userList - list of users that can be managers.
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]);
1339 * Cancels user creation and drops to user screen (either sign).
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();
1348 $('pod-row').loadLastWallpaper();
1350 Oobe
.showScreen({id
: SCREEN_ACCOUNT_PICKER
});
1351 Oobe
.resetSigninUI(true);
1354 if (postCreationPages
.indexOf(this.currentPage_
) >= 0) {
1355 chrome
.send('finishLocalSupervisedUserCreation');
1358 chrome
.send('abortLocalSupervisedUserCreation');
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
1370 this.updateElementText_('created-text-2',
1371 'createSupervisedUserCreatedText2',
1373 loadTimeData
.getString('managementURL')),
1374 this.context_
.supervisedName
);
1375 this.updateElementText_('created-text-3',
1376 'createSupervisedUserCreatedText3',
1378 this.updateElementText_('name-explanation',
1379 'createSupervisedUserNameExplanation',
1383 wrapStrong: function(original
) {
1384 if (original
== undefined)
1386 return '<strong>' + original
+ '</strong>';
1389 updateElementText_: function(localId
, templateName
) {
1390 var args
= Array
.prototype.slice
.call(arguments
);
1392 this.getScreenElement(localId
).innerHTML
=
1393 loadTimeData
.getStringF
.apply(loadTimeData
, args
);
1396 showIntroPage: function() {
1397 $('supervised-user-creation-password').value
= '';
1398 $('supervised-user-creation-password-confirm').value
= '';
1399 $('supervised-user-creation-name').value
= '';
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;
1407 this.setVisiblePage_('intro');
1410 showManagerPage: function() {
1411 this.setVisiblePage_('manager');
1414 showUsernamePage: function() {
1415 this.setVisiblePage_('username');
1418 showTutorialPage: function() {
1419 this.setVisiblePage_('created');
1422 showPage: function(page
) {
1423 this.setVisiblePage_(page
);
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');
1434 showManagerPasswordError: function() {
1435 this.disabled
= false;
1436 this.showSelectedManagerPasswordError_();
1440 TODO(antrim) : this is an explicit code duplications with UserImageScreen.
1441 It should be removed by issue 251179.
1444 * Currently selected user image index (take photo button is with zero
1448 selectedUserImage_
: -1,
1451 setDefaultImages: function(imagesData
) {
1452 var imageGrid
= this.getScreenElement('image-grid');
1453 imageGrid
.setDefaultImages(imagesData
);
1454 this.imagesData_
= imagesData
;
1458 handleActivate_: function() {
1459 var imageGrid
= this.getScreenElement('image-grid');
1460 if (imageGrid
.selectedItemUrl
== ButtonImages
.TAKE_PHOTO
) {
1461 this.handleTakePhoto_();
1464 this.nextButtonPressed_();
1468 * Handles selection change.
1469 * @param {Event} e Selection change event.
1472 handleSelect_: function(e
) {
1473 var imageGrid
= this.getScreenElement('image-grid');
1474 this.updateNextButtonForUser_();
1476 $('supervised-user-creation-flip-photo').tabIndex
=
1477 (imageGrid
.selectionType
== 'camera') ? 0 : -1;
1478 if (imageGrid
.cameraLive
|| imageGrid
.selectionType
!= 'camera')
1479 imageGrid
.previewElement
.classList
.remove('phototaken');
1481 imageGrid
.previewElement
.classList
.add('phototaken');
1483 if (!imageGrid
.cameraLive
|| imageGrid
.selectionType
!= 'camera') {
1484 this.context_
.selectedImageUrl
= imageGrid
.selectedItemUrl
;
1485 chrome
.send('supervisedUserSelectImage',
1486 [imageGrid
.selectedItemUrl
, imageGrid
.selectionType
]);
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(
1496 // Start capture if camera is still the selected item.
1497 $('supervised-user-creation-image-preview-img').classList
.
1498 toggle('animated-transform', true);
1499 return imageGrid
.selectedItem
== imageGrid
.cameraImage
;
1502 $('supervised-user-creation-image-preview-img').classList
.toggle(
1503 'animated-transform', false);
1504 imageGrid
.stopCamera();
1510 * Handle camera-photo flip.
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
));
1522 * Handle photo capture from the live camera stream.
1524 handleTakePhoto_: function(e
) {
1525 this.getScreenElement('image-grid').takePhoto();
1526 chrome
.send('supervisedUserTakePhoto');
1529 handlePhotoTaken_: function(e
) {
1530 chrome
.send('supervisedUserPhotoTaken', [e
.dataURL
]);
1531 announceAccessibleMessage(
1532 loadTimeData
.getString('photoCaptureAccessibleText'));
1536 * Handle photo updated event.
1537 * @param {Event} e Event with 'dataURL' property containing a data URL.
1539 handlePhotoUpdated_: function(e
) {
1540 chrome
.send('supervisedUserPhotoTaken', [e
.dataURL
]);
1544 * Handle discarding the captured photo.
1546 handleDiscardPhoto_: function(e
) {
1547 var imageGrid
= this.getScreenElement('image-grid');
1548 imageGrid
.discardPhoto();
1549 chrome
.send('supervisedUserDiscardPhoto');
1550 announceAccessibleMessage(
1551 loadTimeData
.getString('photoDiscardAccessibleText'));
1554 setCameraPresent: function(present
) {
1555 this.getScreenElement('image-grid').cameraPresent
= present
;
1558 setExistingSupervisedUsers: function(users
) {
1559 var selectedUser
= null;
1560 // Store selected user
1561 if (this.importList_
.selectedPod
)
1562 selectedUser
= this.importList_
.selectedPod
.user
.id
;
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'});
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
)
1581 if (userList
.length
== 1)
1582 this.importList_
.selectPod(this.importList_
.pods
[0]);
1584 if (selectedIndex
>= 0)
1585 this.importList_
.selectPod(this.importList_
.pods
[selectedIndex
]);
1587 if (this.currentPage_
== 'username')
1588 this.getScreenElement('import-link').hidden
= (userList
.length
== 0);