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');
620 $('supervised-user-creation-close-button-item').addEventListener(
621 'click', function(e
) {
630 * Creates button for adding to controls.
631 * @param {string} buttonId -- id for button, have to be unique within
632 * screen. Actual id will be prefixed with screen name and appended with
633 * '-button'. Use getScreenButton(buttonId) to find it later.
634 * @param {string} i18nPrefix -- screen prefix for i18n values.
635 * @param {function} callback -- will be called on button press with
636 * buttonId parameter.
637 * @param {array} pages -- list of pages where this button should be
639 * @param {array} classes -- list of additional CSS classes for button.
641 makeButton: function(buttonId
, i18nPrefix
, callback
, pages
, classes
) {
642 var capitalizedId
= buttonId
.charAt(0).toUpperCase() + buttonId
.slice(1);
643 this.buttonIds
.push(buttonId
);
644 var result
= this.ownerDocument
.createElement('button');
645 result
.id
= this.name() + '-' + buttonId
+ '-button';
646 result
.classList
.add('screen-control-button');
647 for (var i
= 0; i
< classes
.length
; i
++) {
648 result
.classList
.add(classes
[i
]);
650 result
.textContent
= loadTimeData
.
651 getString(i18nPrefix
+ capitalizedId
+ 'ButtonTitle');
652 result
.addEventListener('click', function(e
) {
656 result
.pages
= pages
;
661 * Simple validator for |configureTextInput|.
662 * Element is considered valid if it has any text.
663 * @param {Element} element - element to be validated.
664 * @return {boolean} - true, if element has any text.
666 validIfNotEmpty_: function(element
) {
667 return (element
.value
.length
> 0);
671 * Configure text-input |element|.
672 * @param {Element} element - element to be configured.
673 * @param {function(element)} inputChangeListener - function that will be
674 * called upon any button press/release.
675 * @param {function(element)} validator - function that will be called when
676 * Enter is pressed. If it returns |true| then advance to next element.
677 * @param {function(element)} moveFocus - function that will determine next
678 * element and move focus to it.
679 * @param {function(element)} errorHider - function that is called upon
680 * every button press, so that any associated error can be hidden.
682 configureTextInput: function(element
,
687 element
.addEventListener('keydown', function(e
) {
688 if (e
.keyIdentifier
== 'Enter') {
689 var dataValid
= true;
691 dataValid
= validator(element
);
703 if (inputChangeListener
)
704 inputChangeListener(element
);
706 element
.addEventListener('keyup', function(e
) {
707 if (inputChangeListener
)
708 inputChangeListener(element
);
713 * Makes element from template.
714 * @param {string} templateId -- template will be looked up within screen
715 * by class with name "template-<templateId>".
716 * @param {string} elementId -- id for result, uinque within screen. Actual
717 * id will be prefixed with screen name. Use getScreenElement(id) to find
720 makeFromTemplate: function(templateId
, elementId
) {
721 var templateClassName
= 'template-' + templateId
;
722 var templateNode
= this.querySelector('.' + templateClassName
);
723 var screenPrefix
= this.name() + '-';
724 var result
= templateNode
.cloneNode(true);
725 result
.classList
.remove(templateClassName
);
726 result
.id
= screenPrefix
+ elementId
;
731 * @param {string} buttonId -- id of button to be found,
732 * @return {Element} button created by makeButton with given buttonId.
734 getScreenButton: function(buttonId
) {
735 var fullId
= this.name() + '-' + buttonId
+ '-button';
736 return this.getScreenElement(buttonId
+ '-button');
740 * @param {string} elementId -- id of element to be found,
741 * @return {Element} button created by makeFromTemplate with elementId.
743 getScreenElement: function(elementId
) {
744 var fullId
= this.name() + '-' + elementId
;
750 * @type {!Array} Array of Buttons.
753 var links
= this.ownerDocument
.createElement('div');
754 var buttons
= this.ownerDocument
.createElement('div');
755 links
.classList
.add('controls-links');
756 buttons
.classList
.add('controls-buttons');
758 var importLink
= this.makeFromTemplate('import-supervised-user-link',
760 importLink
.hidden
= true;
761 links
.appendChild(importLink
);
763 var linkElement
= importLink
.querySelector('.signin-link');
764 linkElement
.addEventListener('click',
765 this.importLinkPressed_
.bind(this));
767 var createLink
= this.makeFromTemplate('create-supervised-user-link',
769 createLink
.hidden
= true;
770 links
.appendChild(createLink
);
772 var status
= this.makeFromTemplate('status-container', 'status');
773 buttons
.appendChild(status
);
775 linkElement
= createLink
.querySelector('.signin-link');
776 linkElement
.addEventListener('click',
777 this.createLinkPressed_
.bind(this));
779 buttons
.appendChild(this.makeButton(
781 'supervisedUserCreationFlow',
782 this.startButtonPressed_
.bind(this),
784 ['custom-appearance', 'button-fancy', 'button-blue']));
786 buttons
.appendChild(this.makeButton(
788 'supervisedUserCreationFlow',
789 this.prevButtonPressed_
.bind(this),
793 buttons
.appendChild(this.makeButton(
795 'supervisedUserCreationFlow',
796 this.nextButtonPressed_
.bind(this),
797 ['manager', 'username'],
800 buttons
.appendChild(this.makeButton(
802 'supervisedUserCreationFlow',
803 this.importButtonPressed_
.bind(this),
804 ['import', 'import-password'],
807 buttons
.appendChild(this.makeButton(
809 'supervisedUserCreationFlow',
810 this.gotItButtonPressed_
.bind(this),
812 ['custom-appearance', 'button-fancy', 'button-blue']));
813 return [links
, buttons
];
817 * Does sanity check and calls backend with current user name/password pair
818 * to authenticate manager. May result in showManagerPasswordError.
821 validateAndLogInAsManager_: function() {
822 var selectedPod
= this.managerList_
.selectedPod_
;
823 if (null == selectedPod
)
826 var managerId
= selectedPod
.user
.username
;
827 var managerDisplayId
= selectedPod
.user
.emailAddress
;
828 var managerPassword
= selectedPod
.passwordElement
.value
;
829 if (managerPassword
.length
== 0)
833 this.disabled
= true;
834 this.context_
.managerId
= managerId
;
835 this.context_
.managerDisplayId
= managerDisplayId
;
836 this.context_
.managerName
= selectedPod
.user
.displayName
;
837 chrome
.send('authenticateManagerInSupervisedUserCreationFlow',
838 [managerId
, managerPassword
]);
842 * Does sanity check and calls backend with user display name/password pair
846 validateAndCreateSupervisedUser_: function() {
847 var firstPassword
= $('supervised-user-creation-password').value
;
849 $('supervised-user-creation-password-confirm').value
;
850 var userName
= $('supervised-user-creation-name').value
;
851 if (firstPassword
!= secondPassword
) {
852 this.showPasswordError(loadTimeData
.getString(
853 'createSupervisedUserPasswordMismatchError'));
858 this.disabled
= true;
860 this.context_
.supervisedName
= userName
;
861 chrome
.send('specifySupervisedUserCreationFlowUserData',
862 [userName
, firstPassword
]);
866 * Does sanity check and calls backend with selected existing supervised
867 * user id to import user.
870 importSupervisedUser_: function() {
873 if (this.currentPage_
== 'import-password') {
874 var firstPassword
= this.getScreenElement('password').value
;
875 var secondPassword
= this.getScreenElement('password-confirm').value
;
876 if (firstPassword
!= secondPassword
) {
877 this.showPasswordError(loadTimeData
.getString(
878 'createSupervisedUserPasswordMismatchError'));
881 var userId
= this.context_
.importUserId
;
882 this.disabled
= true;
883 chrome
.send('importSupervisedUserWithPassword',
884 [userId
, firstPassword
]);
887 var selectedPod
= this.importList_
.selectedPod_
;
890 var user
= selectedPod
.user
;
891 var userId
= user
.id
;
893 this.context_
.importUserId
= userId
;
894 this.context_
.supervisedName
= user
.name
;
895 this.context_
.selectedImageUrl
= user
.avatarurl
;
896 if (!user
.needPassword
) {
897 this.disabled
= true;
898 chrome
.send('importSupervisedUser', [userId
]);
900 this.setVisiblePage_('import-password');
906 * Calls backend part to check if current user name is valid/not taken.
907 * Results in a call to either supervisedUserNameOk or
908 * supervisedUserNameError.
911 checkUserName_: function() {
912 var userName
= this.getScreenElement('name').value
;
915 if (userName
== this.lastIncorrectUserName_
||
916 userName
== this.lastVerifiedName_
) {
919 if (userName
.length
> 0) {
920 chrome
.send('checkSupervisedUserName', [userName
]);
922 this.nameErrorVisible
= false;
923 this.lastVerifiedName_
= null;
924 this.lastIncorrectUserName_
= null;
925 this.updateNextButtonForUser_();
930 * Called by backend part in case of successful name validation.
931 * @param {string} name - name that was validated.
933 supervisedUserNameOk: function(name
) {
934 this.lastVerifiedName_
= name
;
935 this.lastIncorrectUserName_
= null;
936 if ($('supervised-user-creation-name').value
== name
)
937 this.clearUserNameError_();
938 this.updateNextButtonForUser_();
942 * Called by backend part in case of name validation failure.
943 * @param {string} name - name that was validated.
944 * @param {string} errorText - reason why this name is invalid.
946 supervisedUserNameError: function(name
, errorText
) {
947 this.disabled
= false;
948 this.lastIncorrectUserName_
= name
;
949 this.lastVerifiedName_
= null;
951 var userNameField
= $('supervised-user-creation-name');
952 if (userNameField
.value
== this.lastIncorrectUserName_
) {
953 this.nameErrorVisible
= true;
954 $('bubble').showTextForElement(
955 $('supervised-user-creation-name'),
957 cr
.ui
.Bubble
.Attachment
.RIGHT
,
959 this.setButtonDisabledStatus('next', true);
963 supervisedUserSuggestImport: function(name
, user_id
) {
964 this.disabled
= false;
965 this.lastIncorrectUserName_
= name
;
966 this.lastVerifiedName_
= null;
968 var userNameField
= $('supervised-user-creation-name');
969 var creationScreen
= this;
971 if (userNameField
.value
== this.lastIncorrectUserName_
) {
972 this.nameErrorVisible
= true;
973 var link
= this.ownerDocument
.createElement('div');
974 link
.innerHTML
= loadTimeData
.getStringF(
976 '<a class="signin-link" href="#">',
979 link
.querySelector('.signin-link').addEventListener('click',
981 creationScreen
.handleSuggestImport_(user_id
);
984 $('bubble').showContentForElement(
985 $('supervised-user-creation-name'),
986 cr
.ui
.Bubble
.Attachment
.RIGHT
,
989 this.setButtonDisabledStatus('next', true);
994 * Clears user name error, if name is no more guaranteed to be invalid.
997 clearUserNameError_: function() {
999 if ($('supervised-user-creation-name').value
==
1000 this.lastIncorrectUserName_
) {
1003 this.nameErrorVisible
= false;
1007 * Called by backend part in case of password validation failure.
1008 * @param {string} errorText - reason why this password is invalid.
1010 showPasswordError: function(errorText
) {
1011 $('bubble').showTextForElement(
1012 $('supervised-user-creation-password'),
1014 cr
.ui
.Bubble
.Attachment
.RIGHT
,
1016 $('supervised-user-creation-password').classList
.add('password-error');
1017 $('supervised-user-creation-password').focus();
1018 this.disabled
= false;
1019 this.setButtonDisabledStatus('next', true);
1023 * True if user name error should be displayed.
1026 set nameErrorVisible(value
) {
1027 $('supervised-user-creation-name').
1028 classList
.toggle('duplicate-name', value
);
1034 * Updates state of Continue button after minimal checks.
1035 * @return {boolean} true, if form seems to be valid.
1038 updateNextButtonForManager_: function() {
1039 var selectedPod
= this.managerList_
.selectedPod_
;
1040 canProceed
= null != selectedPod
&&
1041 selectedPod
.passwordElement
.value
.length
> 0;
1043 this.setButtonDisabledStatus('next', !canProceed
);
1048 * Updates state of Continue button after minimal checks.
1049 * @return {boolean} true, if form seems to be valid.
1052 updateNextButtonForUser_: function() {
1053 var firstPassword
= this.getScreenElement('password').value
;
1054 var secondPassword
= this.getScreenElement('password-confirm').value
;
1055 var userName
= this.getScreenElement('name').value
;
1057 var passwordOk
= (firstPassword
.length
> 0) &&
1058 (firstPassword
.length
== secondPassword
.length
);
1060 if (this.currentPage_
== 'import-password') {
1061 this.setButtonDisabledStatus('import', !passwordOk
);
1064 var imageGrid
= this.getScreenElement('image-grid');
1065 var imageChosen
= !(imageGrid
.selectionType
== 'camera' &&
1066 imageGrid
.cameraLive
);
1069 (userName
.length
> 0) &&
1070 this.lastVerifiedName_
&&
1071 (userName
== this.lastVerifiedName_
) &&
1074 this.setButtonDisabledStatus('next', !canProceed
);
1078 showSelectedManagerPasswordError_: function() {
1079 var selectedPod
= this.managerList_
.selectedPod_
;
1080 selectedPod
.showPasswordError();
1081 selectedPod
.passwordElement
.value
= '';
1082 selectedPod
.focusInput();
1083 this.updateNextButtonForManager_();
1087 * Enables one particular subpage and hides the rest.
1088 * @param {string} visiblePage - name of subpage.
1091 setVisiblePage_: function(visiblePage
) {
1092 this.disabled
= false;
1095 if (!this.imagesRequested_
) {
1096 chrome
.send('supervisedUserGetImages');
1097 this.imagesRequested_
= true;
1099 var pageNames
= ['intro',
1105 var pageButtons
= {'intro' : 'start',
1107 'import' : 'import',
1108 'import-password' : 'import',
1109 'created' : 'gotit'};
1111 var pageToDisplay
= visiblePage
;
1112 if (visiblePage
== 'import-password')
1113 pageToDisplay
= 'username';
1115 for (i
in pageNames
) {
1116 var pageName
= pageNames
[i
];
1117 var page
= $('supervised-user-creation-' + pageName
);
1118 page
.hidden
= (pageName
!= pageToDisplay
);
1119 if (pageName
== pageToDisplay
)
1120 $('step-logo').hidden
= page
.classList
.contains('step-no-logo');
1123 for (i
in this.buttonIds
) {
1124 var button
= this.getScreenButton(this.buttonIds
[i
]);
1125 button
.hidden
= button
.pages
.indexOf(visiblePage
) < 0;
1126 button
.disabled
= false;
1129 var pagesWithCancel
= ['intro', 'manager', 'username', 'import-password',
1131 $('login-header-bar').allowCancel
=
1132 pagesWithCancel
.indexOf(visiblePage
) > -1;
1133 $('cancel-add-user-button').disabled
= false;
1135 this.getScreenElement('import-link').hidden
= true;
1136 this.getScreenElement('create-link').hidden
= true;
1138 if (pageButtons
[visiblePage
])
1139 this.getScreenButton(pageButtons
[visiblePage
]).focus();
1141 this.currentPage_
= visiblePage
;
1143 if (visiblePage
== 'manager' || visiblePage
== 'intro') {
1144 $('supervised-user-creation-password').classList
.remove(
1146 if (this.managerList_
.pods
.length
> 0)
1147 this.managerList_
.selectPod(this.managerList_
.pods
[0]);
1148 $('login-header-bar').updateUI_();
1151 if (visiblePage
== 'username' || visiblePage
== 'import-password') {
1152 var elements
= this.getScreenElement(pageToDisplay
).
1153 querySelectorAll('.hide-on-import');
1154 for (var i
= 0; i
< elements
.length
; i
++) {
1155 elements
[i
].classList
.toggle('hidden-on-import',
1156 visiblePage
== 'import-password');
1159 if (visiblePage
== 'username') {
1160 var imageGrid
= this.getScreenElement('image-grid');
1161 // select some image.
1162 var selected
= this.imagesData_
[
1163 Math
.floor(Math
.random() * this.imagesData_
.length
)];
1164 this.context_
.selectedImageUrl
= selected
.url
;
1165 imageGrid
.selectedItemUrl
= selected
.url
;
1166 chrome
.send('supervisedUserSelectImage',
1167 [selected
.url
, 'default']);
1168 this.getScreenElement('image-grid').redraw();
1169 this.checkUserName_();
1170 this.updateNextButtonForUser_();
1171 this.getScreenElement('name').focus();
1172 this.getScreenElement('import-link').hidden
=
1173 this.importList_
.pods
.length
== 0;
1174 } else if (visiblePage
== 'import-password') {
1175 var imageGrid
= this.getScreenElement('image-grid');
1177 if ('selectedImageUrl' in this.context_
) {
1178 selected
= this.context_
.selectedImageUrl
;
1180 // select some image.
1181 selected
= this.imagesData_
[
1182 Math
.floor(Math
.random() * this.imagesData_
.length
)].url
;
1183 chrome
.send('supervisedUserSelectImage',
1184 [selected
, 'default']);
1186 imageGrid
.selectedItemUrl
= selected
;
1187 this.getScreenElement('image-grid').redraw();
1189 this.updateNextButtonForUser_();
1191 this.getScreenElement('password').focus();
1192 this.getScreenElement('import-link').hidden
= true;
1194 this.getScreenElement('image-grid').stopCamera();
1196 if (visiblePage
== 'import') {
1197 this.getScreenElement('create-link').hidden
= false;
1198 this.getScreenButton('import').disabled
=
1199 !this.importList_
.selectedPod_
||
1200 this.importList_
.selectedPod_
.user
.exists
;
1202 $('supervised-user-creation-close-button-item').hidden
=
1203 (visiblePage
== 'created');
1205 chrome
.send('currentSupervisedUserPage', [this.currentPage_
]);
1208 setButtonDisabledStatus: function(buttonName
, status
) {
1209 var button
= $('supervised-user-creation-' + buttonName
+ '-button');
1210 button
.disabled
= status
;
1213 gotItButtonPressed_: function() {
1214 chrome
.send('finishLocalSupervisedUserCreation');
1217 handleErrorButtonPressed_: function() {
1218 chrome
.send('abortLocalSupervisedUserCreation');
1221 startButtonPressed_: function() {
1222 this.setVisiblePage_('manager');
1223 this.setButtonDisabledStatus('next', true);
1226 nextButtonPressed_: function() {
1227 if (this.currentPage_
== 'manager') {
1228 this.validateAndLogInAsManager_();
1231 if (this.currentPage_
== 'username') {
1232 this.validateAndCreateSupervisedUser_();
1236 importButtonPressed_: function() {
1237 this.importSupervisedUser_();
1240 importLinkPressed_: function() {
1241 this.setVisiblePage_('import');
1244 handleSuggestImport_: function(user_id
) {
1245 this.setVisiblePage_('import');
1246 this.importList_
.selectUser(user_id
);
1249 createLinkPressed_: function() {
1250 this.setVisiblePage_('username');
1251 this.lastIncorrectUserName_
= null;
1252 this.lastVerifiedName_
= null;
1253 this.checkUserName_();
1256 prevButtonPressed_: function() {
1257 this.setVisiblePage_('intro');
1260 showProgress: function(text
) {
1261 var status
= this.getScreenElement('status');
1262 var statusText
= status
.querySelector('.id-text');
1263 statusText
.textContent
= text
;
1264 statusText
.classList
.remove('error');
1265 status
.querySelector('.id-spinner').hidden
= false;
1266 status
.hidden
= false;
1267 this.getScreenElement('import-link').hidden
= true;
1268 this.getScreenElement('create-link').hidden
= true;
1271 showStatusError: function(text
) {
1272 var status
= this.getScreenElement('status');
1273 var statusText
= status
.querySelector('.id-text');
1274 statusText
.textContent
= text
;
1275 statusText
.classList
.add('error');
1276 status
.querySelector('.id-spinner').hidden
= true;
1277 status
.hidden
= false;
1278 this.getScreenElement('import-link').hidden
= true;
1279 this.getScreenElement('create-link').hidden
= true;
1282 hideStatus_: function() {
1283 var status
= this.getScreenElement('status');
1284 status
.hidden
= true;
1288 * Updates state of login header so that necessary buttons are displayed.
1290 onBeforeShow: function(data
) {
1291 $('login-header-bar').signinUIState
=
1292 SIGNIN_UI_STATE
.SUPERVISED_USER_CREATION_FLOW
;
1293 if (data
['managers']) {
1294 this.loadManagers(data
['managers']);
1296 var imageGrid
= this.getScreenElement('image-grid');
1297 imageGrid
.updateAndFocus();
1301 * Update state of login header so that necessary buttons are displayed.
1303 onBeforeHide: function() {
1304 $('login-header-bar').signinUIState
= SIGNIN_UI_STATE
.HIDDEN
;
1305 this.getScreenElement('image-grid').stopCamera();
1309 * Returns a control which should receive an initial focus.
1311 get defaultControl() {
1312 return $('supervised-user-creation-name');
1316 * True if the the screen is disabled (handles no user interaction).
1322 return this.disabled_
;
1325 set disabled(value
) {
1326 this.disabled_
= value
;
1327 var controls
= this.querySelectorAll('button,input');
1328 for (var i
= 0, control
; control
= controls
[i
]; ++i
) {
1329 control
.disabled
= value
;
1331 $('login-header-bar').disabled
= value
;
1332 $('cancel-add-user-button').disabled
= false;
1336 * Called by backend part to propagate list of possible managers.
1337 * @param {Array} userList - list of users that can be managers.
1339 loadManagers: function(userList
) {
1340 $('supervised-user-creation-managers-block').hidden
= false;
1341 this.managerList_
.clearPods();
1342 for (var i
= 0; i
< userList
.length
; ++i
)
1343 this.managerList_
.addPod(userList
[i
]);
1344 if (userList
.length
> 0)
1345 this.managerList_
.selectPod(this.managerList_
.pods
[0]);
1349 * Cancels user creation and drops to user screen (either sign).
1351 cancel: function() {
1352 var notSignedInPages
= ['intro', 'manager'];
1353 var postCreationPages
= ['created'];
1354 if (notSignedInPages
.indexOf(this.currentPage_
) >= 0) {
1355 chrome
.send('hideLocalSupervisedUserCreation');
1357 // Make sure no manager password is kept:
1358 this.managerList_
.clearPods();
1360 Oobe
.showUserPods();
1363 if (postCreationPages
.indexOf(this.currentPage_
) >= 0) {
1364 chrome
.send('finishLocalSupervisedUserCreation');
1367 chrome
.send('abortLocalSupervisedUserCreation');
1370 updateText_: function() {
1371 var managerDisplayId
= this.context_
.managerDisplayId
;
1372 this.updateElementText_('intro-alternate-text',
1373 'createSupervisedUserIntroAlternateText');
1374 this.updateElementText_('created-text-1',
1375 'createSupervisedUserCreatedText1',
1376 this.context_
.supervisedName
);
1377 // TODO(antrim): Move wrapping with strong in grd file, and eliminate this
1379 this.updateElementText_('created-text-2',
1380 'createSupervisedUserCreatedText2',
1382 loadTimeData
.getString('managementURL')),
1383 this.context_
.supervisedName
);
1384 this.updateElementText_('created-text-3',
1385 'createSupervisedUserCreatedText3',
1387 this.updateElementText_('name-explanation',
1388 'createSupervisedUserNameExplanation',
1392 wrapStrong: function(original
) {
1393 if (original
== undefined)
1395 return '<strong>' + original
+ '</strong>';
1398 updateElementText_: function(localId
, templateName
) {
1399 var args
= Array
.prototype.slice
.call(arguments
);
1401 this.getScreenElement(localId
).innerHTML
=
1402 loadTimeData
.getStringF
.apply(loadTimeData
, args
);
1405 showIntroPage: function() {
1406 $('supervised-user-creation-password').value
= '';
1407 $('supervised-user-creation-password-confirm').value
= '';
1408 $('supervised-user-creation-name').value
= '';
1410 this.lastVerifiedName_
= null;
1411 this.lastIncorrectUserName_
= null;
1412 this.passwordErrorVisible
= false;
1413 $('supervised-user-creation-password').classList
.remove('password-error');
1414 this.nameErrorVisible
= false;
1416 this.setVisiblePage_('intro');
1419 showManagerPage: function() {
1420 this.setVisiblePage_('manager');
1423 showUsernamePage: function() {
1424 this.setVisiblePage_('username');
1427 showTutorialPage: function() {
1428 this.setVisiblePage_('created');
1431 showPage: function(page
) {
1432 this.setVisiblePage_(page
);
1435 showErrorPage: function(errorTitle
, errorText
, errorButtonText
) {
1436 this.disabled
= false;
1437 $('supervised-user-creation-error-title').innerHTML
= errorTitle
;
1438 $('supervised-user-creation-error-text').innerHTML
= errorText
;
1439 $('supervised-user-creation-error-button').textContent
= errorButtonText
;
1440 this.setVisiblePage_('error');
1443 showManagerPasswordError: function() {
1444 this.disabled
= false;
1445 this.showSelectedManagerPasswordError_();
1449 TODO(antrim) : this is an explicit code duplications with UserImageScreen.
1450 It should be removed by issue 251179.
1453 * Currently selected user image index (take photo button is with zero
1457 selectedUserImage_
: -1,
1460 setDefaultImages: function(imagesData
) {
1461 var imageGrid
= this.getScreenElement('image-grid');
1462 imageGrid
.setDefaultImages(imagesData
);
1463 this.imagesData_
= imagesData
;
1467 handleActivate_: function() {
1468 var imageGrid
= this.getScreenElement('image-grid');
1469 if (imageGrid
.selectedItemUrl
== ButtonImages
.TAKE_PHOTO
) {
1470 this.handleTakePhoto_();
1473 this.nextButtonPressed_();
1477 * Handles selection change.
1478 * @param {Event} e Selection change event.
1481 handleSelect_: function(e
) {
1482 var imageGrid
= this.getScreenElement('image-grid');
1483 this.updateNextButtonForUser_();
1485 $('supervised-user-creation-flip-photo').tabIndex
=
1486 (imageGrid
.selectionType
== 'camera') ? 0 : -1;
1487 if (imageGrid
.cameraLive
|| imageGrid
.selectionType
!= 'camera')
1488 imageGrid
.previewElement
.classList
.remove('phototaken');
1490 imageGrid
.previewElement
.classList
.add('phototaken');
1492 if (!imageGrid
.cameraLive
|| imageGrid
.selectionType
!= 'camera') {
1493 this.context_
.selectedImageUrl
= imageGrid
.selectedItemUrl
;
1494 chrome
.send('supervisedUserSelectImage',
1495 [imageGrid
.selectedItemUrl
, imageGrid
.selectionType
]);
1497 // Start/stop camera on (de)selection.
1498 if (!imageGrid
.inProgramSelection
&&
1499 imageGrid
.selectionType
!= e
.oldSelectionType
) {
1500 if (imageGrid
.selectionType
== 'camera') {
1501 // Programmatic selection of camera item is done in
1502 // startCamera callback where streaming is started by itself.
1503 imageGrid
.startCamera(
1505 // Start capture if camera is still the selected item.
1506 $('supervised-user-creation-image-preview-img').classList
.
1507 toggle('animated-transform', true);
1508 return imageGrid
.selectedItem
== imageGrid
.cameraImage
;
1511 $('supervised-user-creation-image-preview-img').classList
.toggle(
1512 'animated-transform', false);
1513 imageGrid
.stopCamera();
1519 * Handle camera-photo flip.
1521 handleFlipPhoto_: function() {
1522 var imageGrid
= this.getScreenElement('image-grid');
1523 imageGrid
.previewElement
.classList
.add('animation');
1524 imageGrid
.flipPhoto
= !imageGrid
.flipPhoto
;
1525 var flipMessageId
= imageGrid
.flipPhoto
?
1526 'photoFlippedAccessibleText' : 'photoFlippedBackAccessibleText';
1527 announceAccessibleMessage(loadTimeData
.getString(flipMessageId
));
1531 * Handle photo capture from the live camera stream.
1533 handleTakePhoto_: function(e
) {
1534 this.getScreenElement('image-grid').takePhoto();
1535 chrome
.send('supervisedUserTakePhoto');
1538 handlePhotoTaken_: function(e
) {
1539 chrome
.send('supervisedUserPhotoTaken', [e
.dataURL
]);
1540 announceAccessibleMessage(
1541 loadTimeData
.getString('photoCaptureAccessibleText'));
1545 * Handle photo updated event.
1546 * @param {Event} e Event with 'dataURL' property containing a data URL.
1548 handlePhotoUpdated_: function(e
) {
1549 chrome
.send('supervisedUserPhotoTaken', [e
.dataURL
]);
1553 * Handle discarding the captured photo.
1555 handleDiscardPhoto_: function(e
) {
1556 var imageGrid
= this.getScreenElement('image-grid');
1557 imageGrid
.discardPhoto();
1558 chrome
.send('supervisedUserDiscardPhoto');
1559 announceAccessibleMessage(
1560 loadTimeData
.getString('photoDiscardAccessibleText'));
1563 setCameraPresent: function(present
) {
1564 this.getScreenElement('image-grid').cameraPresent
= present
;
1567 setExistingSupervisedUsers: function(users
) {
1568 var selectedUser
= null;
1569 // Store selected user
1570 if (this.importList_
.selectedPod
)
1571 selectedUser
= this.importList_
.selectedPod
.user
.id
;
1573 var userList
= users
;
1574 userList
.sort(function(a
, b
) {
1575 // Put existing users last.
1576 if (a
.exists
!= b
.exists
)
1577 return a
.exists
? 1 : -1;
1578 // Sort rest by name.
1579 return a
.name
.localeCompare(b
.name
, [], {sensitivity
: 'base'});
1582 this.importList_
.clearPods();
1583 var selectedIndex
= -1;
1584 for (var i
= 0; i
< userList
.length
; ++i
) {
1585 this.importList_
.addPod(userList
[i
]);
1586 if (selectedUser
== userList
[i
].id
)
1590 if (userList
.length
== 1)
1591 this.importList_
.selectPod(this.importList_
.pods
[0]);
1593 if (selectedIndex
>= 0)
1594 this.importList_
.selectPod(this.importList_
.pods
[selectedIndex
]);
1596 if (this.currentPage_
== 'username')
1597 this.getScreenElement('import-link').hidden
= (userList
.length
== 0);