blob: 372b744a0f231e874e653f1a8e2d29f3c4e1244e [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Locally managed user creation flow screen.
*/
login.createScreen('LocallyManagedUserCreationScreen',
'managed-user-creation', function() {
var MAX_NAME_LENGTH = 50;
var UserImagesGrid = options.UserImagesGrid;
var ButtonImages = UserImagesGrid.ButtonImages;
var ManagerPod = cr.ui.define(function() {
var node = $('managed-user-creation-manager-template').cloneNode(true);
node.removeAttribute('id');
node.removeAttribute('hidden');
return node;
});
ManagerPod.userImageSalt_ = {};
/**
* UI element for displaying single account in list of possible managers for
* new locally managed user.
* @type {Object}
*/
ManagerPod.prototype = {
__proto__: HTMLDivElement.prototype,
/** @override */
decorate: function() {
// Mousedown has to be used instead of click to be able to prevent 'focus'
// event later.
this.addEventListener('mousedown',
this.handleMouseDown_.bind(this));
var screen = $('managed-user-creation');
var managerPod = this;
var managerPodList = screen.managerList_;
var hideManagerPasswordError = function(element) {
managerPod.passwordElement.classList.remove('password-error');
$('bubble').hide();
};
screen.configureTextInput(
this.passwordElement,
screen.updateNextButtonForManager_.bind(screen),
screen.validIfNotEmpty_.bind(screen),
function(element) {
screen.getScreenButton('next').focus();
},
hideManagerPasswordError);
this.passwordElement.addEventListener('keydown', function(e) {
switch (e.keyIdentifier) {
case 'Up':
managerPodList.selectNextPod(-1);
e.stopPropagation();
break;
case 'Down':
managerPodList.selectNextPod(+1);
e.stopPropagation();
break;
}
});
},
/**
* Updates UI elements from user data.
*/
update: function() {
this.imageElement.src = 'chrome://userimage/' + this.user.username +
'?id=' + ManagerPod.userImageSalt_[this.user.username];
this.nameElement.textContent = this.user.displayName;
this.emailElement.textContent = this.user.emailAddress;
},
showPasswordError: function() {
this.passwordElement.classList.add('password-error');
$('bubble').showTextForElement(
this.passwordElement,
loadTimeData.getString('createManagedUserWrongManagerPasswordText'),
cr.ui.Bubble.Attachment.BOTTOM,
24, 4);
},
/**
* Brings focus to password field.
*/
focusInput: function() {
this.passwordElement.focus();
},
/**
* Gets image element.
* @type {!HTMLImageElement}
*/
get imageElement() {
return this.querySelector('.managed-user-creation-manager-image');
},
/**
* Gets name element.
* @type {!HTMLDivElement}
*/
get nameElement() {
return this.querySelector('.managed-user-creation-manager-name');
},
/**
* Gets e-mail element.
* @type {!HTMLDivElement}
*/
get emailElement() {
return this.querySelector('.managed-user-creation-manager-email');
},
/**
* Gets password element.
* @type {!HTMLDivElement}
*/
get passwordElement() {
return this.querySelector('.managed-user-creation-manager-password');
},
/**
* Gets password enclosing block.
* @type {!HTMLDivElement}
*/
get passwordBlock() {
return this.querySelector('.password-block');
},
/** @override */
handleMouseDown_: function(e) {
this.parentNode.selectPod(this);
// Prevent default so that we don't trigger 'focus' event.
e.preventDefault();
},
/**
* The user that this pod represents.
* @type {!Object}
*/
user_: undefined,
get user() {
return this.user_;
},
set user(userDict) {
this.user_ = userDict;
this.update();
},
};
var ManagerPodList = cr.ui.define('div');
/**
* UI element for selecting manager account for new managed user.
* @type {Object}
*/
ManagerPodList.prototype = {
__proto__: HTMLDivElement.prototype,
selectedPod_: null,
/** @override */
decorate: function() {
},
/**
* Returns all the pods in this pod list.
* @type {NodeList}
*/
get pods() {
return this.children;
},
addPod: function(manager) {
var managerPod = new ManagerPod({user: manager});
this.appendChild(managerPod);
managerPod.update();
},
clearPods: function() {
this.innerHTML = '';
this.selectedPod_ = null;
},
selectPod: function(podToSelect) {
if ((this.selectedPod_ == podToSelect) && !!podToSelect) {
podToSelect.focusInput();
return;
}
this.selectedPod_ = podToSelect;
for (var i = 0, pod; pod = this.pods[i]; ++i) {
if (pod != podToSelect) {
pod.classList.remove('focused');
pod.passwordElement.value = '';
pod.passwordBlock.hidden = true;
}
}
if (!podToSelect)
return;
podToSelect.classList.add('focused');
podToSelect.passwordBlock.hidden = false;
podToSelect.passwordElement.value = '';
podToSelect.focusInput();
chrome.send('managerSelectedOnLocallyManagedUserCreationFlow',
[podToSelect.user.username]);
},
/**
* Select pod next to currently selected one in given |direction|.
* @param {integer} direction - +1 for selecting pod below current, -1 for
* selecting pod above current.
* @type {boolean} returns if selected pod has changed.
*/
selectNextPod: function(direction) {
if (!this.selectedPod_)
return false;
var index = -1;
for (var i = 0, pod; pod = this.pods[i]; ++i) {
if (pod == this.selectedPod_) {
index = i;
break;
}
}
if (-1 == index)
return false;
index = index + direction;
if (index < 0 || index >= this.pods.length)
return false;
this.selectPod(this.pods[index]);
return true;
}
};
var ImportPod = cr.ui.define(function() {
var node = $('managed-user-creation-import-template').cloneNode(true);
node.removeAttribute('id');
node.removeAttribute('hidden');
return node;
});
/**
* UI element for displaying single supervised user in list of possible users
* for importing existing users.
* @type {Object}
*/
ImportPod.prototype = {
__proto__: HTMLDivElement.prototype,
/** @override */
decorate: function() {
// Mousedown has to be used instead of click to be able to prevent 'focus'
// event later.
this.addEventListener('mousedown',
this.handleMouseDown_.bind(this));
},
/**
* Updates UI elements from user data.
*/
update: function() {
this.imageElement.src = this.user.avatarurl;
this.nameElement.textContent = this.user.name;
if (this.user.exists) {
if (this.user.conflict == 'imported') {
this.nameElement.textContent =
loadTimeData.getStringF('importUserExists', this.user.name);
} else {
this.nameElement.textContent =
loadTimeData.getStringF('importUsernameExists', this.user.name);
}
}
this.classList.toggle('imported', this.user.exists);
},
/**
* Gets image element.
* @type {!HTMLImageElement}
*/
get imageElement() {
return this.querySelector('.import-pod-image');
},
/**
* Gets name element.
* @type {!HTMLDivElement}
*/
get nameElement() {
return this.querySelector('.import-pod-name');
},
/** @override */
handleMouseDown_: function(e) {
this.parentNode.selectPod(this);
// Prevent default so that we don't trigger 'focus' event.
e.preventDefault();
},
/**
* The user that this pod represents.
* @type {Object}
*/
user_: undefined,
get user() {
return this.user_;
},
set user(userDict) {
this.user_ = userDict;
this.update();
},
};
var ImportPodList = cr.ui.define('div');
/**
* UI element for selecting existing supervised user for import.
* @type {Object}
*/
ImportPodList.prototype = {
__proto__: HTMLDivElement.prototype,
selectedPod_: null,
/** @override */
decorate: function() {
},
/**
* Returns all the pods in this pod list.
* @type {NodeList}
*/
get pods() {
return this.children;
},
addPod: function(user) {
var importPod = new ImportPod({user: user});
this.appendChild(importPod);
importPod.update();
},
clearPods: function() {
this.innerHTML = '';
this.selectedPod_ = null;
},
scrollIntoView: function(pod) {
scroller = this.parentNode;
var itemHeight = pod.getBoundingClientRect().height;
var scrollTop = scroller.scrollTop;
var top = pod.offsetTop - scroller.offsetTop;
var clientHeight = scroller.clientHeight;
var self = scroller;
// Function to adjust the tops of viewport and row.
function scrollToAdjustTop() {
self.scrollTop = top;
return true;
};
// Function to adjust the bottoms of viewport and row.
function scrollToAdjustBottom() {
var cs = getComputedStyle(self);
var paddingY = parseInt(cs.paddingTop, 10) +
parseInt(cs.paddingBottom, 10);
if (top + itemHeight > scrollTop + clientHeight - paddingY) {
self.scrollTop = top + itemHeight - clientHeight + paddingY;
return true;
}
return false;
};
// Check if the entire of given indexed row can be shown in the viewport.
if (itemHeight <= clientHeight) {
if (top < scrollTop)
return scrollToAdjustTop();
if (scrollTop + clientHeight < top + itemHeight)
return scrollToAdjustBottom();
} else {
if (scrollTop < top)
return scrollToAdjustTop();
if (top + itemHeight < scrollTop + clientHeight)
return scrollToAdjustBottom();
}
return false;
},
/**
* @param {Element} podToSelect - pod to select, can be null.
*/
selectPod: function(podToSelect) {
if ((this.selectedPod_ == podToSelect) && !!podToSelect) {
return;
}
this.selectedPod_ = podToSelect;
for (var i = 0; i < this.pods.length; i++) {
var pod = this.pods[i];
if (pod != podToSelect)
pod.classList.remove('focused');
}
if (!podToSelect)
return;
podToSelect.classList.add('focused');
var screen = $('managed-user-creation');
if (!this.selectedPod_) {
screen.getScreenButton('import').disabled = true;
} else {
screen.getScreenButton('import').disabled =
this.selectedPod_.user.exists;
if (!this.selectedPod_.user.exists) {
chrome.send('userSelectedForImportInManagedUserCreationFlow',
[podToSelect.user.id]);
}
}
},
selectUser: function(user_id) {
for (var i = 0, pod; pod = this.pods[i]; ++i) {
if (pod.user.id == user_id) {
this.selectPod(pod);
this.scrollIntoView(pod);
break;
}
}
},
};
return {
EXTERNAL_API: [
'loadManagers',
'managedUserSuggestImport',
'managedUserNameError',
'managedUserNameOk',
'showErrorPage',
'showIntroPage',
'showManagerPage',
'showManagerPasswordError',
'showPasswordError',
'showProgress',
'showStatusError',
'showTutorialPage',
'showUsernamePage',
'showPage',
'setDefaultImages',
'setCameraPresent',
'setExistingManagedUsers',
],
lastVerifiedName_: null,
lastIncorrectUserName_: null,
managerList_: null,
importList_: null,
currentPage_: null,
imagesRequested_: false,
// Contains data that can be auto-shared with handler.
context_: {},
/** @override */
decorate: function() {
this.managerList_ = new ManagerPodList();
$('managed-user-creation-managers-pane').appendChild(this.managerList_);
this.importList_ = new ImportPodList();
$('managed-user-creation-import-pane').appendChild(this.importList_);
var userNameField = $('managed-user-creation-name');
var passwordField = $('managed-user-creation-password');
var password2Field = $('managed-user-creation-password-confirm');
var creationScreen = this;
var hideUserPasswordError = function(element) {
$('bubble').hide();
$('managed-user-creation-password').classList.remove('password-error');
};
this.configureTextInput(userNameField,
this.checkUserName_.bind(this),
this.validIfNotEmpty_.bind(this),
function(element) {
passwordField.focus();
},
this.clearUserNameError_.bind(this));
this.configureTextInput(passwordField,
this.updateNextButtonForUser_.bind(this),
this.validIfNotEmpty_.bind(this),
function(element) {
password2Field.focus();
},
hideUserPasswordError);
this.configureTextInput(password2Field,
this.updateNextButtonForUser_.bind(this),
this.validIfNotEmpty_.bind(this),
function(element) {
creationScreen.getScreenButton('next').focus();
},
hideUserPasswordError);
this.getScreenButton('error').addEventListener('click', function(e) {
creationScreen.handleErrorButtonPressed_();
e.stopPropagation();
});
/*
TODO(antrim) : this is an explicit code duplications with UserImageScreen.
It should be removed by issue 251179.
*/
var imageGrid = this.getScreenElement('image-grid');
UserImagesGrid.decorate(imageGrid);
// Preview image will track the selected item's URL.
var previewElement = this.getScreenElement('image-preview');
previewElement.oncontextmenu = function(e) { e.preventDefault(); };
imageGrid.previewElement = previewElement;
imageGrid.selectionType = 'default';
imageGrid.flipPhotoElement = this.getScreenElement('flip-photo');
imageGrid.addEventListener('activate',
this.handleActivate_.bind(this));
imageGrid.addEventListener('select',
this.handleSelect_.bind(this));
imageGrid.addEventListener('phototaken',
this.handlePhotoTaken_.bind(this));
imageGrid.addEventListener('photoupdated',
this.handlePhotoUpdated_.bind(this));
// Set the title for camera item in the grid.
imageGrid.setCameraTitles(
loadTimeData.getString('takePhoto'),
loadTimeData.getString('photoFromCamera'));
this.getScreenElement('take-photo').addEventListener(
'click', this.handleTakePhoto_.bind(this));
this.getScreenElement('discard-photo').addEventListener(
'click', this.handleDiscardPhoto_.bind(this));
// Toggle 'animation' class for the duration of WebKit transition.
this.getScreenElement('flip-photo').addEventListener(
'click', function(e) {
previewElement.classList.add('animation');
imageGrid.flipPhoto = !imageGrid.flipPhoto;
});
this.getScreenElement('image-stream-crop').addEventListener(
'webkitTransitionEnd', function(e) {
previewElement.classList.remove('animation');
});
this.getScreenElement('image-preview-img').addEventListener(
'webkitTransitionEnd', function(e) {
previewElement.classList.remove('animation');
});
},
buttonIds: [],
/**
* Creates button for adding to controls.
* @param {string} buttonId -- id for button, have to be unique within
* screen. Actual id will be prefixed with screen name and appended with
* '-button'. Use getScreenButton(buttonId) to find it later.
* @param {string} i18nPrefix -- screen prefix for i18n values.
* @param {function} callback -- will be called on button press with
* buttonId parameter.
* @param {array} pages -- list of pages where this button should be
* displayed.
* @param {array} classes -- list of additional CSS classes for button.
*/
makeButton: function(buttonId, i18nPrefix, callback, pages, classes) {
var capitalizedId = buttonId.charAt(0).toUpperCase() + buttonId.slice(1);
this.buttonIds.push(buttonId);
var result = this.ownerDocument.createElement('button');
result.id = this.name() + '-' + buttonId + '-button';
result.classList.add('screen-control-button');
for (var i = 0; i < classes.length; i++) {
result.classList.add(classes[i]);
}
result.textContent = loadTimeData.
getString(i18nPrefix + capitalizedId + 'ButtonTitle');
result.addEventListener('click', function(e) {
callback(buttonId);
e.stopPropagation();
});
result.pages = pages;
return result;
},
/**
* Simple validator for |configureTextInput|.
* Element is considered valid if it has any text.
* @param {Element} element - element to be validated.
* @return {boolean} - true, if element has any text.
*/
validIfNotEmpty_: function(element) {
return (element.value.length > 0);
},
/**
* Configure text-input |element|.
* @param {Element} element - element to be configured.
* @param {function(element)} inputChangeListener - function that will be
* called upon any button press/release.
* @param {function(element)} validator - function that will be called when
* Enter is pressed. If it returns |true| then advance to next element.
* @param {function(element)} moveFocus - function that will determine next
* element and move focus to it.
* @param {function(element)} errorHider - function that is called upon
* every button press, so that any associated error can be hidden.
*/
configureTextInput: function(element,
inputChangeListener,
validator,
moveFocus,
errorHider) {
element.addEventListener('keydown', function(e) {
if (e.keyIdentifier == 'Enter') {
var dataValid = true;
if (validator)
dataValid = validator(element);
if (!dataValid) {
element.focus();
} else {
if (moveFocus)
moveFocus(element);
}
e.stopPropagation();
return;
}
if (errorHider)
errorHider(element);
if (inputChangeListener)
inputChangeListener(element);
});
element.addEventListener('keyup', function(e) {
if (inputChangeListener)
inputChangeListener(element);
});
},
/**
* Makes element from template.
* @param {string} templateId -- template will be looked up within screen
* by class with name "template-<templateId>".
* @param {string} elementId -- id for result, uinque within screen. Actual
* id will be prefixed with screen name. Use getScreenElement(id) to find
* it later.
*/
makeFromTemplate: function(templateId, elementId) {
var templateClassName = 'template-' + templateId;
var templateNode = this.querySelector('.' + templateClassName);
var screenPrefix = this.name() + '-';
var result = templateNode.cloneNode(true);
result.classList.remove(templateClassName);
result.id = screenPrefix + elementId;
return result;
},
/**
* @param {string} buttonId -- id of button to be found,
* @return {Element} button created by makeButton with given buttonId.
*/
getScreenButton: function(buttonId) {
var fullId = this.name() + '-' + buttonId + '-button';
return this.getScreenElement(buttonId + '-button');
},
/**
* @param {string} elementId -- id of element to be found,
* @return {Element} button created by makeFromTemplate with elementId.
*/
getScreenElement: function(elementId) {
var fullId = this.name() + '-' + elementId;
return $(fullId);
},
/**
* Screen controls.
* @type {!Array} Array of Buttons.
*/
get buttons() {
var buttons = [];
var status = this.makeFromTemplate('status-container', 'status');
buttons.push(status);
var importLink = this.makeFromTemplate('import-supervised-user-link',
'import-link');
importLink.hidden = true;
buttons.push(importLink);
var linkElement = importLink.querySelector('.signin-link');
linkElement.addEventListener('click',
this.importLinkPressed_.bind(this));
var createLink = this.makeFromTemplate('create-supervised-user-link',
'create-link');
createLink.hidden = true;
buttons.push(createLink);
linkElement = createLink.querySelector('.signin-link');
linkElement.addEventListener('click',
this.createLinkPressed_.bind(this));
buttons.push(this.makeButton(
'start',
'managedUserCreationFlow',
this.startButtonPressed_.bind(this),
['intro'],
['custom-appearance', 'button-fancy', 'button-blue']));
buttons.push(this.makeButton(
'prev',
'managedUserCreationFlow',
this.prevButtonPressed_.bind(this),
['manager'],
[]));
buttons.push(this.makeButton(
'next',
'managedUserCreationFlow',
this.nextButtonPressed_.bind(this),
['manager', 'username'],
[]));
buttons.push(this.makeButton(
'import',
'managedUserCreationFlow',
this.importButtonPressed_.bind(this),
['import', 'import-password'],
[]));
buttons.push(this.makeButton(
'gotit',
'managedUserCreationFlow',
this.gotItButtonPressed_.bind(this),
['created'],
['custom-appearance', 'button-fancy', 'button-blue']));
return buttons;
},
/**
* Does sanity check and calls backend with current user name/password pair
* to authenticate manager. May result in showManagerPasswordError.
* @private
*/
validateAndLogInAsManager_: function() {
var selectedPod = this.managerList_.selectedPod_;
if (null == selectedPod)
return;
var managerId = selectedPod.user.username;
var managerDisplayId = selectedPod.user.emailAddress;
var managerPassword = selectedPod.passwordElement.value;
if (managerPassword.length == 0)
return;
if (this.disabled)
return;
this.disabled = true;
this.context_.managerId = managerId;
this.context_.managerDisplayId = managerDisplayId;
this.context_.managerName = selectedPod.user.displayName;
chrome.send('authenticateManagerInLocallyManagedUserCreationFlow',
[managerId, managerPassword]);
},
/**
* Does sanity check and calls backend with user display name/password pair
* to create a user.
* @private
*/
validateAndCreateLocallyManagedUser_: function() {
var firstPassword = $('managed-user-creation-password').value;
var secondPassword =
$('managed-user-creation-password-confirm').value;
var userName = $('managed-user-creation-name').value;
if (firstPassword != secondPassword) {
this.showPasswordError(
loadTimeData.getString('createManagedUserPasswordMismatchError'));
return;
}
if (this.disabled)
return;
this.disabled = true;
this.context_.managedName = userName;
chrome.send('specifyLocallyManagedUserCreationFlowUserData',
[userName, firstPassword]);
},
/**
* Does sanity check and calls backend with selected existing supervised
* user id to import user.
* @private
*/
importSupervisedUser_: function() {
if (this.disabled)
return;
if (this.currentPage_ == 'import-password') {
var firstPassword = this.getScreenElement('password').value;
var secondPassword = this.getScreenElement('password-confirm').value;
if (firstPassword != secondPassword) {
this.showPasswordError(
loadTimeData.getString('createManagedUserPasswordMismatchError'));
return;
}
var userId = this.context_.importUserId;
this.disabled = true;
chrome.send('importSupervisedUserWithPassword',
[userId, firstPassword]);
return;
} else {
var selectedPod = this.importList_.selectedPod_;
if (!selectedPod)
return;
var user = selectedPod.user;
var userId = user.id;
this.context_.importUserId = userId;
this.context_.managedName = user.name;
this.context_.selectedImageUrl = user.avatarurl;
if (!user.needPassword) {
this.disabled = true;
chrome.send('importSupervisedUser', [userId]);
} else {
this.setVisiblePage_('import-password');
}
}
},
/**
* Calls backend part to check if current user name is valid/not taken.
* Results in call to either managedUserNameOk or managedUserNameError.
* @private
*/
checkUserName_: function() {
var userName = this.getScreenElement('name').value;
// Avoid flickering
if (userName == this.lastIncorrectUserName_ ||
userName == this.lastVerifiedName_) {
return;
}
if (userName.length > 0) {
chrome.send('checkLocallyManagedUserName', [userName]);
} else {
this.nameErrorVisible = false;
this.lastVerifiedName_ = null;
this.lastIncorrectUserName_ = null;
this.updateNextButtonForUser_();
}
},
/**
* Called by backend part in case of successful name validation.
* @param {string} name - name that was validated.
*/
managedUserNameOk: function(name) {
this.lastVerifiedName_ = name;
this.lastIncorrectUserName_ = null;
if ($('managed-user-creation-name').value == name)
this.clearUserNameError_();
this.updateNextButtonForUser_();
},
/**
* Called by backend part in case of name validation failure.
* @param {string} name - name that was validated.
* @param {string} errorText - reason why this name is invalid.
*/
managedUserNameError: function(name, errorText) {
this.disabled = false;
this.lastIncorrectUserName_ = name;
this.lastVerifiedName_ = null;
var userNameField = $('managed-user-creation-name');
if (userNameField.value == this.lastIncorrectUserName_) {
this.nameErrorVisible = true;
$('bubble').showTextForElement(
$('managed-user-creation-name'),
errorText,
cr.ui.Bubble.Attachment.RIGHT,
12, 4);
this.setButtonDisabledStatus('next', true);
}
},
managedUserSuggestImport: function(name, user_id) {
this.disabled = false;
this.lastIncorrectUserName_ = name;
this.lastVerifiedName_ = null;
var userNameField = $('managed-user-creation-name');
var creationScreen = this;
if (userNameField.value == this.lastIncorrectUserName_) {
this.nameErrorVisible = true;
var link = this.ownerDocument.createElement('div');
link.innerHTML = loadTimeData.getStringF(
'importBubbleText',
'<a class="signin-link" href="#">',
name,
'</a>');
link.querySelector('.signin-link').addEventListener('click',
function(e) {
creationScreen.handleSuggestImport_(user_id);
e.stopPropagation();
});
$('bubble').showContentForElement(
$('managed-user-creation-name'),
cr.ui.Bubble.Attachment.RIGHT,
link,
12, 4);
this.setButtonDisabledStatus('next', true);
}
},
/**
* Clears user name error, if name is no more guaranteed to be invalid.
* @private
*/
clearUserNameError_: function() {
// Avoid flickering
if ($('managed-user-creation-name').value ==
this.lastIncorrectUserName_) {
return;
}
this.nameErrorVisible = false;
},
/**
* Called by backend part in case of password validation failure.
* @param {string} errorText - reason why this password is invalid.
*/
showPasswordError: function(errorText) {
$('bubble').showTextForElement(
$('managed-user-creation-password'),
errorText,
cr.ui.Bubble.Attachment.RIGHT,
12, 4);
$('managed-user-creation-password').classList.add('password-error');
$('managed-user-creation-password').focus();
this.disabled = false;
this.setButtonDisabledStatus('next', true);
},
/**
* True if user name error should be displayed.
* @type {boolean}
*/
set nameErrorVisible(value) {
$('managed-user-creation-name').
classList.toggle('duplicate-name', value);
if (!value)
$('bubble').hide();
},
/**
* Updates state of Continue button after minimal checks.
* @return {boolean} true, if form seems to be valid.
* @private
*/
updateNextButtonForManager_: function() {
var selectedPod = this.managerList_.selectedPod_;
canProceed = null != selectedPod &&
selectedPod.passwordElement.value.length > 0;
this.setButtonDisabledStatus('next', !canProceed);
return canProceed;
},
/**
* Updates state of Continue button after minimal checks.
* @return {boolean} true, if form seems to be valid.
* @private
*/
updateNextButtonForUser_: function() {
var firstPassword = this.getScreenElement('password').value;
var secondPassword = this.getScreenElement('password-confirm').value;
var userName = this.getScreenElement('name').value;
var passwordOk = (firstPassword.length > 0) &&
(firstPassword.length == secondPassword.length);
if (this.currentPage_ == 'import-password') {
this.setButtonDisabledStatus('import', !passwordOk);
return passwordOk;
}
var imageGrid = this.getScreenElement('image-grid');
var imageChosen = !(imageGrid.selectionType == 'camera' &&
imageGrid.cameraLive);
var canProceed =
passwordOk &&
(userName.length > 0) &&
this.lastVerifiedName_ &&
(userName == this.lastVerifiedName_) &&
imageChosen;
this.setButtonDisabledStatus('next', !canProceed);
return canProceed;
},
showSelectedManagerPasswordError_: function() {
var selectedPod = this.managerList_.selectedPod_;
selectedPod.showPasswordError();
selectedPod.passwordElement.value = '';
selectedPod.focusInput();
this.updateNextButtonForManager_();
},
/**
* Enables one particular subpage and hides the rest.
* @param {string} visiblePage - name of subpage.
* @private
*/
setVisiblePage_: function(visiblePage) {
this.disabled = false;
this.updateText_();
$('bubble').hide();
if (!this.imagesRequested_) {
chrome.send('supervisedUserGetImages');
this.imagesRequested_ = true;
}
var pageNames = ['intro',
'manager',
'username',
'import',
'error',
'created'];
var pageButtons = {'intro' : 'start',
'error' : 'error',
'import' : 'import',
'import-password' : 'import',
'created' : 'gotit'};
this.hideStatus_();
var pageToDisplay = visiblePage;
if (visiblePage == 'import-password')
pageToDisplay = 'username';
for (i in pageNames) {
var pageName = pageNames[i];
var page = $('managed-user-creation-' + pageName);
page.hidden = (pageName != pageToDisplay);
if (pageName == pageToDisplay)
$('step-logo').hidden = page.classList.contains('step-no-logo');
}
for (i in this.buttonIds) {
var button = this.getScreenButton(this.buttonIds[i]);
button.hidden = button.pages.indexOf(visiblePage) < 0;
button.disabled = false;
}
var pagesWithCancel = ['intro', 'manager', 'username', 'import-password',
'error', 'import'];
var cancelButton = $('cancel-add-user-button');
cancelButton.hidden = pagesWithCancel.indexOf(visiblePage) < 0;
cancelButton.disabled = false;
this.getScreenElement('import-link').hidden = true;
this.getScreenElement('create-link').hidden = true;
if (pageButtons[visiblePage])
this.getScreenButton(pageButtons[visiblePage]).focus();
this.currentPage_ = visiblePage;
if (visiblePage == 'manager' || visiblePage == 'intro') {
$('managed-user-creation-password').classList.remove('password-error');
if (this.managerList_.pods.length > 0)
this.managerList_.selectPod(this.managerList_.pods[0]);
}
if (visiblePage == 'username' || visiblePage == 'import-password') {
var elements = this.getScreenElement(pageToDisplay).
querySelectorAll('.hide-on-import');
for (var i = 0; i < elements.length; i++) {
elements[i].classList.toggle('hidden-on-import',
visiblePage == 'import-password');
}
}
if (visiblePage == 'username') {
var imageGrid = this.getScreenElement('image-grid');
// select some image.
var selected = this.imagesData_[
Math.floor(Math.random() * this.imagesData_.length)];
this.context_.selectedImageUrl = selected.url;
imageGrid.selectedItemUrl = selected.url;
chrome.send('supervisedUserSelectImage',
[selected.url, 'default']);
this.getScreenElement('image-grid').redraw();
this.checkUserName_();
this.updateNextButtonForUser_();
this.getScreenElement('name').focus();
this.getScreenElement('import-link').hidden =
this.importList_.pods.length == 0;
} else if (visiblePage == 'import-password') {
var imageGrid = this.getScreenElement('image-grid');
var selected;
if ('selectedImageUrl' in this.context_) {
selected = this.context_.selectedImageUrl;
} else {
// select some image.
selected = this.imagesData_[
Math.floor(Math.random() * this.imagesData_.length)].url;
chrome.send('supervisedUserSelectImage',
[selected, 'default']);
}
imageGrid.selectedItemUrl = selected;
this.getScreenElement('image-grid').redraw();
this.updateNextButtonForUser_();
this.getScreenElement('password').focus();
this.getScreenElement('import-link').hidden = true;
} else {
this.getScreenElement('image-grid').stopCamera();
}
if (visiblePage == 'import') {
this.getScreenElement('create-link').hidden = false;
this.getScreenButton('import').disabled =
!this.importList_.selectedPod_ ||
this.importList_.selectedPod_.user.exists;
}
chrome.send('currentSupervisedUserPage', [this.currentPage_]);
},
setButtonDisabledStatus: function(buttonName, status) {
var button = $('managed-user-creation-' + buttonName + '-button');
button.disabled = status;
},
gotItButtonPressed_: function() {
chrome.send('finishLocalManagedUserCreation');
},
handleErrorButtonPressed_: function() {
chrome.send('abortLocalManagedUserCreation');
},
startButtonPressed_: function() {
this.setVisiblePage_('manager');
this.setButtonDisabledStatus('next', true);
},
nextButtonPressed_: function() {
if (this.currentPage_ == 'manager') {
this.validateAndLogInAsManager_();
return;
}
if (this.currentPage_ == 'username') {
this.validateAndCreateLocallyManagedUser_();
}
},
importButtonPressed_: function() {
this.importSupervisedUser_();
},
importLinkPressed_: function() {
this.setVisiblePage_('import');
},
handleSuggestImport_: function(user_id) {
this.setVisiblePage_('import');
this.importList_.selectUser(user_id);
},
createLinkPressed_: function() {
this.setVisiblePage_('username');
this.lastIncorrectUserName_ = null;
this.lastVerifiedName_ = null;
this.checkUserName_();
},
prevButtonPressed_: function() {
this.setVisiblePage_('intro');
},
showProgress: function(text) {
var status = this.getScreenElement('status');
var statusText = status.querySelector('.id-text');
statusText.textContent = text;
statusText.classList.remove('error');
status.querySelector('.id-spinner').hidden = false;
status.hidden = false;
this.getScreenElement('import-link').hidden = true;
this.getScreenElement('create-link').hidden = true;
},
showStatusError: function(text) {
var status = this.getScreenElement('status');
var statusText = status.querySelector('.id-text');
statusText.textContent = text;
statusText.classList.add('error');
status.querySelector('.id-spinner').hidden = true;
status.hidden = false;
this.getScreenElement('import-link').hidden = true;
this.getScreenElement('create-link').hidden = true;
},
hideStatus_: function() {
var status = this.getScreenElement('status');
status.hidden = true;
},
/**
* Updates state of login header so that necessary buttons are displayed.
**/
onBeforeShow: function(data) {
$('login-header-bar').signinUIState =
SIGNIN_UI_STATE.MANAGED_USER_CREATION_FLOW;
if (data['managers']) {
this.loadManagers(data['managers']);
}
var imageGrid = this.getScreenElement('image-grid');
imageGrid.updateAndFocus();
},
/**
* Update state of login header so that necessary buttons are displayed.
*/
onBeforeHide: function() {
$('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
this.getScreenElement('image-grid').stopCamera();
},
/**
* Returns a control which should receive an initial focus.
*/
get defaultControl() {
return $('managed-user-creation-name');
},
/**
* True if the the screen is disabled (handles no user interaction).
* @type {boolean}
*/
disabled_: false,
get disabled() {
return this.disabled_;
},
set disabled(value) {
this.disabled_ = value;
var controls = this.querySelectorAll('button,input');
for (var i = 0, control; control = controls[i]; ++i) {
control.disabled = value;
}
$('login-header-bar').disabled = value;
},
/**
* Called by backend part to propagate list of possible managers.
* @param {Array} userList - list of users that can be managers.
*/
loadManagers: function(userList) {
$('managed-user-creation-managers-block').hidden = false;
this.managerList_.clearPods();
for (var i = 0; i < userList.length; ++i)
this.managerList_.addPod(userList[i]);
if (userList.length > 0)
this.managerList_.selectPod(this.managerList_.pods[0]);
},
/**
* Cancels user creation and drops to user screen (either sign).
*/
cancel: function() {
var notSignedInPages = ['intro', 'manager'];
var postCreationPages = ['created'];
if (notSignedInPages.indexOf(this.currentPage_) >= 0) {
// Make sure no manager password is kept:
this.managerList_.clearPods();
$('pod-row').loadLastWallpaper();
Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
Oobe.resetSigninUI(true);
return;
}
if (postCreationPages.indexOf(this.currentPage_) >= 0) {
chrome.send('finishLocalManagedUserCreation');
return;
}
chrome.send('abortLocalManagedUserCreation');
},
updateText_: function() {
var managerDisplayId = this.context_.managerDisplayId;
this.updateElementText_('intro-alternate-text',
'createManagedUserIntroAlternateText');
this.updateElementText_('created-text-1',
'createManagedUserCreatedText1',
this.context_.managedName);
// TODO(antrim): Move wrapping with strong in grd file, and eliminate this
//call.
this.updateElementText_('created-text-2',
'createManagedUserCreatedText2',
this.wrapStrong(
loadTimeData.getString('managementURL')),
this.context_.managedName);
this.updateElementText_('created-text-3',
'createManagedUserCreatedText3',
managerDisplayId);
this.updateElementText_('name-explanation',
'createManagedUserNameExplanation',
managerDisplayId);
},
wrapStrong: function(original) {
if (original == undefined)
return original;
return '<strong>' + original + '</strong>';
},
updateElementText_: function(localId, templateName) {
var args = Array.prototype.slice.call(arguments);
args.shift();
this.getScreenElement(localId).innerHTML =
loadTimeData.getStringF.apply(loadTimeData, args);
},
showIntroPage: function() {
$('managed-user-creation-password').value = '';
$('managed-user-creation-password-confirm').value = '';
$('managed-user-creation-name').value = '';
this.lastVerifiedName_ = null;
this.lastIncorrectUserName_ = null;
this.passwordErrorVisible = false;
$('managed-user-creation-password').classList.remove('password-error');
this.nameErrorVisible = false;
this.setVisiblePage_('intro');
},
showManagerPage: function() {
this.setVisiblePage_('manager');
},
showUsernamePage: function() {
this.setVisiblePage_('username');
},
showTutorialPage: function() {
this.setVisiblePage_('created');
},
showPage: function(page) {
this.setVisiblePage_(page);
},
showErrorPage: function(errorTitle, errorText, errorButtonText) {
this.disabled = false;
$('managed-user-creation-error-title').innerHTML = errorTitle;
$('managed-user-creation-error-text').innerHTML = errorText;
$('managed-user-creation-error-button').textContent = errorButtonText;
this.setVisiblePage_('error');
},
showManagerPasswordError: function() {
this.disabled = false;
this.showSelectedManagerPasswordError_();
},
/*
TODO(antrim) : this is an explicit code duplications with UserImageScreen.
It should be removed by issue 251179.
*/
/**
* Currently selected user image index (take photo button is with zero
* index).
* @type {number}
*/
selectedUserImage_: -1,
imagesData: [],
setDefaultImages: function(imagesData) {
var imageGrid = this.getScreenElement('image-grid');
for (var i = 0, data; data = imagesData[i]; i++) {
var item = imageGrid.addItem(data.url, data.title);
item.type = 'default';
item.author = data.author || '';
item.website = data.website || '';
}
this.imagesData_ = imagesData;
},
handleActivate_: function() {
var imageGrid = this.getScreenElement('image-grid');
if (imageGrid.selectedItemUrl == ButtonImages.TAKE_PHOTO) {
this.handleTakePhoto_();
return;
}
this.nextButtonPressed_();
},
/**
* Handles selection change.
* @param {Event} e Selection change event.
* @private
*/
handleSelect_: function(e) {
var imageGrid = this.getScreenElement('image-grid');
this.updateNextButtonForUser_();
$('managed-user-creation-flip-photo').tabIndex =
(imageGrid.selectionType == 'camera') ? 0 : -1;
if (imageGrid.cameraLive || imageGrid.selectionType != 'camera')
imageGrid.previewElement.classList.remove('phototaken');
else
imageGrid.previewElement.classList.add('phototaken');
if (!imageGrid.cameraLive || imageGrid.selectionType != 'camera') {
this.context_.selectedImageUrl = imageGrid.selectedItemUrl;
chrome.send('supervisedUserSelectImage',
[imageGrid.selectedItemUrl, imageGrid.selectionType]);
}
// Start/stop camera on (de)selection.
if (!imageGrid.inProgramSelection &&
imageGrid.selectionType != e.oldSelectionType) {
if (imageGrid.selectionType == 'camera') {
// Programmatic selection of camera item is done in
// startCamera callback where streaming is started by itself.
imageGrid.startCamera(
function() {
// Start capture if camera is still the selected item.
$('managed-user-creation-image-preview-img').classList.toggle(
'animated-transform', true);
return imageGrid.selectedItem == imageGrid.cameraImage;
});
} else {
$('managed-user-creation-image-preview-img').classList.toggle(
'animated-transform', false);
imageGrid.stopCamera();
}
}
},
/**
* Handle photo capture from the live camera stream.
*/
handleTakePhoto_: function(e) {
this.getScreenElement('image-grid').takePhoto();
},
handlePhotoTaken_: function(e) {
chrome.send('supervisedUserPhotoTaken', [e.dataURL]);
},
/**
* Handle photo updated event.
* @param {Event} e Event with 'dataURL' property containing a data URL.
*/
handlePhotoUpdated_: function(e) {
chrome.send('supervisedUserPhotoTaken', [e.dataURL]);
},
/**
* Handle discarding the captured photo.
*/
handleDiscardPhoto_: function(e) {
var imageGrid = this.getScreenElement('image-grid');
imageGrid.discardPhoto();
},
setCameraPresent: function(present) {
this.getScreenElement('image-grid').cameraPresent = present;
},
setExistingManagedUsers: function(users) {
var userList = users;
userList.sort(function(a, b) {
// Put existing users last.
if (a.exists != b.exists)
return a.exists ? 1 : -1;
// Sort rest by name.
return a.name.localeCompare(b.name, [], {sensitivity: 'base'});
});
this.importList_.clearPods();
for (var i = 0; i < userList.length; ++i)
this.importList_.addPod(userList[i]);
if (userList.length == 1)
this.importList_.selectPod(this.managerList_.pods[0]);
if (userList.length > 0 && this.currentPage_ == 'username')
this.getScreenElement('import-link').hidden = false;
},
};
});