diff options
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/login/user_pod_row.js')
-rw-r--r-- | chromium/chrome/browser/resources/chromeos/login/user_pod_row.js | 1755 |
1 files changed, 0 insertions, 1755 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/login/user_pod_row.js b/chromium/chrome/browser/resources/chromeos/login/user_pod_row.js deleted file mode 100644 index 251aa654892..00000000000 --- a/chromium/chrome/browser/resources/chromeos/login/user_pod_row.js +++ /dev/null @@ -1,1755 +0,0 @@ -// Copyright (c) 2012 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. - -<include src="wallpaper_loader.js"></include> - -/** - * @fileoverview User pod row implementation. - */ - -cr.define('login', function() { - /** - * Number of displayed columns depending on user pod count. - * @type {Array.<number>} - * @const - */ - var COLUMNS = [0, 1, 2, 3, 4, 5, 4, 4, 4, 5, 5, 6, 6, 5, 5, 6, 6, 6, 6]; - - /** - * Mapping between number of columns in pod-row and margin between user pods - * for such layout. - * @type {Array.<number>} - * @const - */ - var MARGIN_BY_COLUMNS = [undefined, 40, 40, 40, 40, 40, 12]; - - /** - * Maximal number of columns currently supported by pod-row. - * @type {number} - * @const - */ - var MAX_NUMBER_OF_COLUMNS = 6; - - /** - * Variables used for pod placement processing. - * Width and height should be synced with computed CSS sizes of pods. - */ - var POD_WIDTH = 180; - var POD_HEIGHT = 217; - var POD_ROW_PADDING = 10; - - /** - * Whether to preselect the first pod automatically on login screen. - * @type {boolean} - * @const - */ - var PRESELECT_FIRST_POD = true; - - /** - * Maximum time for which the pod row remains hidden until all user images - * have been loaded. - * @type {number} - * @const - */ - var POD_ROW_IMAGES_LOAD_TIMEOUT_MS = 3000; - - /** - * Public session help topic identifier. - * @type {number} - * @const - */ - var HELP_TOPIC_PUBLIC_SESSION = 3041033; - - /** - * Oauth token status. These must match UserManager::OAuthTokenStatus. - * @enum {number} - * @const - */ - var OAuthTokenStatus = { - UNKNOWN: 0, - INVALID_OLD: 1, - VALID_OLD: 2, - INVALID_NEW: 3, - VALID_NEW: 4 - }; - - /** - * Tab order for user pods. Update these when adding new controls. - * @enum {number} - * @const - */ - var UserPodTabOrder = { - POD_INPUT: 1, // Password input fields (and whole pods themselves). - HEADER_BAR: 2, // Buttons on the header bar (Shutdown, Add User). - ACTION_BOX: 3, // Action box buttons. - PAD_MENU_ITEM: 4 // User pad menu items (Remove this user). - }; - - // Focus and tab order are organized as follows: - // - // (1) all user pods have tab index 1 so they are traversed first; - // (2) when a user pod is activated, its tab index is set to -1 and its - // main input field gets focus and tab index 1; - // (3) buttons on the header bar have tab index 2 so they follow user pods; - // (4) Action box buttons have tab index 3 and follow header bar buttons; - // (5) lastly, focus jumps to the Status Area and back to user pods. - // - // 'Focus' event is handled by a capture handler for the whole document - // and in some cases 'mousedown' event handlers are used instead of 'click' - // handlers where it's necessary to prevent 'focus' event from being fired. - - /** - * Helper function to remove a class from given element. - * @param {!HTMLElement} el Element whose class list to change. - * @param {string} cl Class to remove. - */ - function removeClass(el, cl) { - el.classList.remove(cl); - } - - /** - * Creates a user pod. - * @constructor - * @extends {HTMLDivElement} - */ - var UserPod = cr.ui.define(function() { - var node = $('user-pod-template').cloneNode(true); - node.removeAttribute('id'); - return node; - }); - - /** - * Stops event propagation from the any user pod child element. - * @param {Event} e Event to handle. - */ - function stopEventPropagation(e) { - // Prevent default so that we don't trigger a 'focus' event. - e.preventDefault(); - e.stopPropagation(); - } - - /** - * Unique salt added to user image URLs to prevent caching. Dictionary with - * user names as keys. - * @type {Object} - */ - UserPod.userImageSalt_ = {}; - - UserPod.prototype = { - __proto__: HTMLDivElement.prototype, - - /** @override */ - decorate: function() { - this.tabIndex = UserPodTabOrder.POD_INPUT; - this.customButton.tabIndex = UserPodTabOrder.POD_INPUT; - this.actionBoxAreaElement.tabIndex = UserPodTabOrder.ACTION_BOX; - - // Mousedown has to be used instead of click to be able to prevent 'focus' - // event later. - this.addEventListener('mousedown', - this.handleMouseDown_.bind(this)); - - this.signinButtonElement.addEventListener('click', - this.activate.bind(this)); - - this.actionBoxAreaElement.addEventListener('mousedown', - stopEventPropagation); - this.actionBoxAreaElement.addEventListener('click', - this.handleActionAreaButtonClick_.bind(this)); - this.actionBoxAreaElement.addEventListener('keydown', - this.handleActionAreaButtonKeyDown_.bind(this)); - - this.actionBoxMenuRemoveElement.addEventListener('click', - this.handleRemoveCommandClick_.bind(this)); - this.actionBoxMenuRemoveElement.addEventListener('keydown', - this.handleRemoveCommandKeyDown_.bind(this)); - this.actionBoxMenuRemoveElement.addEventListener('blur', - this.handleRemoveCommandBlur_.bind(this)); - - if (this.actionBoxRemoveUserWarningButtonElement) { - this.actionBoxRemoveUserWarningButtonElement.addEventListener( - 'click', - this.handleRemoveUserConfirmationClick_.bind(this)); - } - - this.customButton.addEventListener('click', - this.handleCustomButtonClick_.bind(this)); - }, - - /** - * Initializes the pod after its properties set and added to a pod row. - */ - initialize: function() { - this.passwordElement.addEventListener('keydown', - this.parentNode.handleKeyDown.bind(this.parentNode)); - this.passwordElement.addEventListener('keypress', - this.handlePasswordKeyPress_.bind(this)); - - this.imageElement.addEventListener('load', - this.parentNode.handlePodImageLoad.bind(this.parentNode, this)); - }, - - /** - * Resets tab order for pod elements to its initial state. - */ - resetTabOrder: function() { - this.tabIndex = UserPodTabOrder.POD_INPUT; - this.mainInput.tabIndex = -1; - }, - - /** - * Handles keypress event (i.e. any textual input) on password input. - * @param {Event} e Keypress Event object. - * @private - */ - handlePasswordKeyPress_: function(e) { - // When tabbing from the system tray a tab key press is received. Suppress - // this so as not to type a tab character into the password field. - if (e.keyCode == 9) { - e.preventDefault(); - return; - } - }, - - /** - * Top edge margin number of pixels. - * @type {?number} - */ - set top(top) { - this.style.top = cr.ui.toCssPx(top); - }, - /** - * Left edge margin number of pixels. - * @type {?number} - */ - set left(left) { - this.style.left = cr.ui.toCssPx(left); - }, - - /** - * Gets signed in indicator element. - * @type {!HTMLDivElement} - */ - get signedInIndicatorElement() { - return this.querySelector('.signed-in-indicator'); - }, - - /** - * Gets image element. - * @type {!HTMLImageElement} - */ - get imageElement() { - return this.querySelector('.user-image'); - }, - - /** - * Gets name element. - * @type {!HTMLDivElement} - */ - get nameElement() { - return this.querySelector('.name'); - }, - - /** - * Gets password field. - * @type {!HTMLInputElement} - */ - get passwordElement() { - return this.querySelector('.password'); - }, - - /** - * Gets Caps Lock hint image. - * @type {!HTMLImageElement} - */ - get capslockHintElement() { - return this.querySelector('.capslock-hint'); - }, - - /** - * Gets user signin button. - * @type {!HTMLInputElement} - */ - get signinButtonElement() { - return this.querySelector('.signin-button'); - }, - - /** - * Gets action box area. - * @type {!HTMLInputElement} - */ - get actionBoxAreaElement() { - return this.querySelector('.action-box-area'); - }, - - /** - * Gets user type icon area. - * @type {!HTMLInputElement} - */ - get userTypeIconAreaElement() { - return this.querySelector('.user-type-icon-area'); - }, - - /** - * Gets action box menu. - * @type {!HTMLInputElement} - */ - get actionBoxMenuElement() { - return this.querySelector('.action-box-menu'); - }, - - /** - * Gets action box menu title. - * @type {!HTMLInputElement} - */ - get actionBoxMenuTitleElement() { - return this.querySelector('.action-box-menu-title'); - }, - - /** - * Gets action box menu title, user name item. - * @type {!HTMLInputElement} - */ - get actionBoxMenuTitleNameElement() { - return this.querySelector('.action-box-menu-title-name'); - }, - - /** - * Gets action box menu title, user email item. - * @type {!HTMLInputElement} - */ - get actionBoxMenuTitleEmailElement() { - return this.querySelector('.action-box-menu-title-email'); - }, - - /** - * Gets action box menu, remove user command item. - * @type {!HTMLInputElement} - */ - get actionBoxMenuCommandElement() { - return this.querySelector('.action-box-menu-remove-command'); - }, - - /** - * Gets action box menu, remove user command item div. - * @type {!HTMLInputElement} - */ - get actionBoxMenuRemoveElement() { - return this.querySelector('.action-box-menu-remove'); - }, - - /** - * Gets action box menu, remove user command item div. - * @type {!HTMLInputElement} - */ - get actionBoxRemoveUserWarningElement() { - return this.querySelector('.action-box-remove-user-warning'); - }, - - /** - * Gets action box menu, remove user command item div. - * @type {!HTMLInputElement} - */ - get actionBoxRemoveUserWarningButtonElement() { - return this.querySelector( - '.remove-warning-button'); - }, - - /** - * Gets the locked user indicator box. - * @type {!HTMLInputElement} - */ - get lockedIndicatorElement() { - return this.querySelector('.locked-indicator'); - }, - - /** - * Gets the custom button. This button is normally hidden, but can be - * shown using the chrome.screenlockPrivate API. - * @type {!HTMLInputElement} - */ - get customButton() { - return this.querySelector('.custom-button'); - }, - - /** - * Updates the user pod element. - */ - update: function() { - this.imageElement.src = 'chrome://userimage/' + this.user.username + - '?id=' + UserPod.userImageSalt_[this.user.username]; - - this.nameElement.textContent = this.user_.displayName; - this.signedInIndicatorElement.hidden = !this.user_.signedIn; - - var needSignin = this.needSignin; - this.passwordElement.hidden = needSignin; - this.signinButtonElement.hidden = !needSignin; - - this.updateActionBoxArea(); - }, - - updateActionBoxArea: function() { - this.actionBoxAreaElement.hidden = this.user_.publicAccount; - this.actionBoxMenuRemoveElement.hidden = !this.user_.canRemove; - - this.actionBoxAreaElement.setAttribute( - 'aria-label', loadTimeData.getStringF( - 'podMenuButtonAccessibleName', this.user_.emailAddress)); - this.actionBoxMenuRemoveElement.setAttribute( - 'aria-label', loadTimeData.getString( - 'podMenuRemoveItemAccessibleName')); - this.actionBoxMenuTitleNameElement.textContent = this.user_.isOwner ? - loadTimeData.getStringF('ownerUserPattern', this.user_.displayName) : - this.user_.displayName; - this.actionBoxMenuTitleEmailElement.textContent = this.user_.emailAddress; - this.actionBoxMenuTitleEmailElement.hidden = - this.user_.locallyManagedUser; - - this.actionBoxMenuCommandElement.textContent = - loadTimeData.getString('removeUser'); - this.passwordElement.setAttribute('aria-label', loadTimeData.getStringF( - 'passwordFieldAccessibleName', this.user_.emailAddress)); - this.userTypeIconAreaElement.hidden = !this.user_.locallyManagedUser; - }, - - /** - * The user that this pod represents. - * @type {!Object} - */ - user_: undefined, - get user() { - return this.user_; - }, - set user(userDict) { - this.user_ = userDict; - this.update(); - }, - - /** - * Whether signin is required for this user. - */ - get needSignin() { - // Signin is performed if the user has an invalid oauth token and is - // not currently signed in (i.e. not the lock screen). - return this.user.oauthTokenStatus != OAuthTokenStatus.VALID_OLD && - this.user.oauthTokenStatus != OAuthTokenStatus.VALID_NEW && - !this.user.signedIn; - }, - - /** - * Gets main input element. - * @type {(HTMLButtonElement|HTMLInputElement)} - */ - get mainInput() { - if (!this.signinButtonElement.hidden) - return this.signinButtonElement; - else - return this.passwordElement; - }, - - /** - * Whether action box button is in active state. - * @type {boolean} - */ - get isActionBoxMenuActive() { - return this.actionBoxAreaElement.classList.contains('active'); - }, - set isActionBoxMenuActive(active) { - if (active == this.isActionBoxMenuActive) - return; - - if (active) { - this.actionBoxMenuRemoveElement.hidden = !this.user_.canRemove; - if (this.actionBoxRemoveUserWarningElement) - this.actionBoxRemoveUserWarningElement.hidden = true; - - // Clear focus first if another pod is focused. - if (!this.parentNode.isFocused(this)) { - this.parentNode.focusPod(undefined, true); - this.actionBoxAreaElement.focus(); - } - this.actionBoxAreaElement.classList.add('active'); - } else { - this.actionBoxAreaElement.classList.remove('active'); - } - }, - - /** - * Whether action box button is in hovered state. - * @type {boolean} - */ - get isActionBoxMenuHovered() { - return this.actionBoxAreaElement.classList.contains('hovered'); - }, - set isActionBoxMenuHovered(hovered) { - if (hovered == this.isActionBoxMenuHovered) - return; - - if (hovered) { - this.actionBoxAreaElement.classList.add('hovered'); - this.classList.add('hovered'); - } else { - this.actionBoxAreaElement.classList.remove('hovered'); - this.classList.remove('hovered'); - } - }, - - /** - * Updates the image element of the user. - */ - updateUserImage: function() { - UserPod.userImageSalt_[this.user.username] = new Date().getTime(); - this.update(); - }, - - /** - * Focuses on input element. - */ - focusInput: function() { - var needSignin = this.needSignin; - this.signinButtonElement.hidden = !needSignin; - this.passwordElement.hidden = needSignin; - - // Move tabIndex from the whole pod to the main input. - this.tabIndex = -1; - this.mainInput.tabIndex = UserPodTabOrder.POD_INPUT; - this.mainInput.focus(); - }, - - /** - * Activates the pod. - * @return {boolean} True if activated successfully. - */ - activate: function() { - if (!this.signinButtonElement.hidden) { - this.showSigninUI(); - } else if (!this.passwordElement.value) { - return false; - } else { - Oobe.disableSigninUI(); - chrome.send('authenticateUser', - [this.user.username, this.passwordElement.value]); - } - - return true; - }, - - showSupervisedUserSigninWarning: function() { - // Locally managed user token has been invalidated. - // Make sure that pod is focused i.e. "Sign in" button is seen. - this.parentNode.focusPod(this); - - var error = document.createElement('div'); - var messageDiv = document.createElement('div'); - messageDiv.className = 'error-message-bubble'; - messageDiv.textContent = - loadTimeData.getString('supervisedUserExpiredTokenWarning'); - error.appendChild(messageDiv); - - $('bubble').showContentForElement( - this.signinButtonElement, - cr.ui.Bubble.Attachment.TOP, - error, - this.signinButtonElement.offsetWidth / 2, - 4); - }, - - /** - * Shows signin UI for this user. - */ - showSigninUI: function() { - if (this.user.locallyManagedUser) { - this.showSupervisedUserSigninWarning(); - } else { - this.parentNode.showSigninUI(this.user.emailAddress); - } - }, - - /** - * Resets the input field and updates the tab order of pod controls. - * @param {boolean} takeFocus If true, input field takes focus. - */ - reset: function(takeFocus) { - this.passwordElement.value = ''; - if (takeFocus) - this.focusInput(); // This will set a custom tab order. - else - this.resetTabOrder(); - }, - - /** - * Handles a click event on action area button. - * @param {Event} e Click event. - */ - handleActionAreaButtonClick_: function(e) { - if (this.parentNode.disabled) - return; - this.isActionBoxMenuActive = !this.isActionBoxMenuActive; - }, - - /** - * Handles a keydown event on action area button. - * @param {Event} e KeyDown event. - */ - handleActionAreaButtonKeyDown_: function(e) { - if (this.disabled) - return; - switch (e.keyIdentifier) { - case 'Enter': - case 'U+0020': // Space - if (this.parentNode.focusedPod_ && !this.isActionBoxMenuActive) - this.isActionBoxMenuActive = true; - e.stopPropagation(); - break; - case 'Up': - case 'Down': - if (this.isActionBoxMenuActive) { - this.actionBoxMenuRemoveElement.tabIndex = - UserPodTabOrder.PAD_MENU_ITEM; - this.actionBoxMenuRemoveElement.focus(); - } - e.stopPropagation(); - break; - case 'U+001B': // Esc - this.isActionBoxMenuActive = false; - e.stopPropagation(); - break; - case 'U+0009': // Tab - this.parentNode.focusPod(); - default: - this.isActionBoxMenuActive = false; - break; - } - }, - - /** - * Handles a click event on remove user command. - * @param {Event} e Click event. - */ - handleRemoveCommandClick_: function(e) { - if (this.user.locallyManagedUser || this.user.isDesktopUser) { - this.showRemoveWarning_(); - return; - } - if (this.isActionBoxMenuActive) - chrome.send('removeUser', [this.user.username]); - }, - - /** - * Shows remove warning for managed users. - */ - showRemoveWarning_: function() { - this.actionBoxMenuRemoveElement.hidden = true; - this.actionBoxRemoveUserWarningElement.hidden = false; - }, - - /** - * Handles a click event on remove user confirmation button. - * @param {Event} e Click event. - */ - handleRemoveUserConfirmationClick_: function(e) { - if (this.isActionBoxMenuActive) - chrome.send('removeUser', [this.user.username]); - }, - - /** - * Handles a keydown event on remove command. - * @param {Event} e KeyDown event. - */ - handleRemoveCommandKeyDown_: function(e) { - if (this.disabled) - return; - switch (e.keyIdentifier) { - case 'Enter': - chrome.send('removeUser', [this.user.username]); - e.stopPropagation(); - break; - case 'Up': - case 'Down': - e.stopPropagation(); - break; - case 'U+001B': // Esc - this.actionBoxAreaElement.focus(); - this.isActionBoxMenuActive = false; - e.stopPropagation(); - break; - default: - this.actionBoxAreaElement.focus(); - this.isActionBoxMenuActive = false; - break; - } - }, - - /** - * Handles a blur event on remove command. - * @param {Event} e Blur event. - */ - handleRemoveCommandBlur_: function(e) { - if (this.disabled) - return; - this.actionBoxMenuRemoveElement.tabIndex = -1; - }, - - /** - * Handles mousedown event on a user pod. - * @param {Event} e Mousedown event. - */ - handleMouseDown_: function(e) { - if (this.parentNode.disabled) - return; - - if (!this.signinButtonElement.hidden && !this.isActionBoxMenuActive) { - this.showSigninUI(); - // Prevent default so that we don't trigger 'focus' event. - e.preventDefault(); - } - }, - - /** - * Called when the custom button is clicked. - */ - handleCustomButtonClick_: function() { - chrome.send('customButtonClicked', [this.user.username]); - } - }; - - /** - * Creates a public account user pod. - * @constructor - * @extends {UserPod} - */ - var PublicAccountUserPod = cr.ui.define(function() { - var node = UserPod(); - - var extras = $('public-account-user-pod-extras-template').children; - for (var i = 0; i < extras.length; ++i) { - var el = extras[i].cloneNode(true); - node.appendChild(el); - } - - return node; - }); - - PublicAccountUserPod.prototype = { - __proto__: UserPod.prototype, - - /** - * "Enter" button in expanded side pane. - * @type {!HTMLButtonElement} - */ - get enterButtonElement() { - return this.querySelector('.enter-button'); - }, - - /** - * Boolean flag of whether the pod is showing the side pane. The flag - * controls whether 'expanded' class is added to the pod's class list and - * resets tab order because main input element changes when the 'expanded' - * state changes. - * @type {boolean} - */ - get expanded() { - return this.classList.contains('expanded'); - }, - set expanded(expanded) { - if (this.expanded == expanded) - return; - - this.resetTabOrder(); - this.classList.toggle('expanded', expanded); - - var self = this; - this.classList.add('animating'); - this.addEventListener('webkitTransitionEnd', function f(e) { - self.removeEventListener('webkitTransitionEnd', f); - self.classList.remove('animating'); - - // Accessibility focus indicator does not move with the focused - // element. Sends a 'focus' event on the currently focused element - // so that accessibility focus indicator updates its location. - if (document.activeElement) - document.activeElement.dispatchEvent(new Event('focus')); - }); - }, - - /** @override */ - get needSignin() { - return false; - }, - - /** @override */ - get mainInput() { - if (this.expanded) - return this.enterButtonElement; - else - return this.nameElement; - }, - - /** @override */ - decorate: function() { - UserPod.prototype.decorate.call(this); - - this.classList.remove('need-password'); - this.classList.add('public-account'); - - this.nameElement.addEventListener('keydown', (function(e) { - if (e.keyIdentifier == 'Enter') { - this.parentNode.activatedPod = this; - // Stop this keydown event from bubbling up to PodRow handler. - e.stopPropagation(); - // Prevent default so that we don't trigger a 'click' event on the - // newly focused "Enter" button. - e.preventDefault(); - } - }).bind(this)); - - var learnMore = this.querySelector('.learn-more'); - learnMore.addEventListener('mousedown', stopEventPropagation); - learnMore.addEventListener('click', this.handleLearnMoreEvent); - learnMore.addEventListener('keydown', this.handleLearnMoreEvent); - - learnMore = this.querySelector('.side-pane-learn-more'); - learnMore.addEventListener('click', this.handleLearnMoreEvent); - learnMore.addEventListener('keydown', this.handleLearnMoreEvent); - - this.enterButtonElement.addEventListener('click', (function(e) { - this.enterButtonElement.disabled = true; - chrome.send('launchPublicAccount', [this.user.username]); - }).bind(this)); - }, - - /** - * Updates the user pod element. - */ - update: function() { - UserPod.prototype.update.call(this); - this.querySelector('.side-pane-name').textContent = - this.user_.displayName; - this.querySelector('.info').textContent = - loadTimeData.getStringF('publicAccountInfoFormat', - this.user_.enterpriseDomain); - }, - - /** @override */ - focusInput: function() { - // Move tabIndex from the whole pod to the main input. - this.tabIndex = -1; - this.mainInput.tabIndex = UserPodTabOrder.POD_INPUT; - this.mainInput.focus(); - }, - - /** @override */ - reset: function(takeFocus) { - if (!takeFocus) - this.expanded = false; - this.enterButtonElement.disabled = false; - UserPod.prototype.reset.call(this, takeFocus); - }, - - /** @override */ - activate: function() { - this.expanded = true; - this.focusInput(); - return true; - }, - - /** @override */ - handleMouseDown_: function(e) { - if (this.parentNode.disabled) - return; - - this.parentNode.focusPod(this); - this.parentNode.activatedPod = this; - // Prevent default so that we don't trigger 'focus' event. - e.preventDefault(); - }, - - /** - * Handle mouse and keyboard events for the learn more button. - * Triggering the button causes information about public sessions to be - * shown. - * @param {Event} event Mouse or keyboard event. - */ - handleLearnMoreEvent: function(event) { - switch (event.type) { - // Show informaton on left click. Let any other clicks propagate. - case 'click': - if (event.button != 0) - return; - break; - // Show informaton when <Return> or <Space> is pressed. Let any other - // key presses propagate. - case 'keydown': - switch (event.keyCode) { - case 13: // Return. - case 32: // Space. - break; - default: - return; - } - break; - } - chrome.send('launchHelpApp', [HELP_TOPIC_PUBLIC_SESSION]); - stopEventPropagation(event); - }, - }; - - /** - * Creates a user pod to be used only in desktop chrome. - * @constructor - * @extends {UserPod} - */ - var DesktopUserPod = cr.ui.define(function() { - // Don't just instantiate a UserPod(), as this will call decorate() on the - // parent object, and add duplicate event listeners. - var node = $('user-pod-template').cloneNode(true); - node.removeAttribute('id'); - return node; - }); - - DesktopUserPod.prototype = { - __proto__: UserPod.prototype, - - /** @override */ - get mainInput() { - if (!this.passwordElement.hidden) - return this.passwordElement; - else - return this.nameElement; - }, - - /** @override */ - decorate: function() { - UserPod.prototype.decorate.call(this); - }, - - /** @override */ - update: function() { - // TODO(noms): Use the actual profile avatar for local profiles once the - // new, non-pixellated avatars are available. - this.imageElement.src = this.user.emailAddress == '' ? - 'chrome://theme/IDR_USER_MANAGER_DEFAULT_AVATAR' : - this.user.userImage; - this.nameElement.textContent = this.user.displayName; - - var isLockedUser = this.user.needsSignin; - this.signinButtonElement.hidden = true; - this.lockedIndicatorElement.hidden = !isLockedUser; - this.passwordElement.hidden = !isLockedUser; - this.nameElement.hidden = isLockedUser; - - UserPod.prototype.updateActionBoxArea.call(this); - }, - - /** @override */ - focusInput: function() { - // For focused pods, display the name unless the pod is locked. - var isLockedUser = this.user.needsSignin; - this.signinButtonElement.hidden = true; - this.lockedIndicatorElement.hidden = !isLockedUser; - this.passwordElement.hidden = !isLockedUser; - this.nameElement.hidden = isLockedUser; - - // Move tabIndex from the whole pod to the main input. - this.tabIndex = -1; - this.mainInput.tabIndex = UserPodTabOrder.POD_INPUT; - this.mainInput.focus(); - }, - - /** @override */ - reset: function(takeFocus) { - // Always display the user's name for unfocused pods. - if (!takeFocus) - this.nameElement.hidden = false; - UserPod.prototype.reset.call(this, takeFocus); - }, - - /** @override */ - activate: function() { - if (this.passwordElement.hidden) { - Oobe.launchUser(this.user.emailAddress, this.user.displayName); - } else if (!this.passwordElement.value) { - return false; - } else { - chrome.send('authenticatedLaunchUser', - [this.user.emailAddress, - this.user.displayName, - this.passwordElement.value]); - } - this.passwordElement.value = ''; - return true; - }, - - /** @override */ - handleMouseDown_: function(e) { - if (this.parentNode.disabled) - return; - - Oobe.clearErrors(); - this.parentNode.lastFocusedPod_ = this; - - // If this is an unlocked pod, then open a browser window. Otherwise - // just activate the pod and show the password field. - if (!this.user.needsSignin && !this.isActionBoxMenuActive) - this.activate(); - }, - - /** @override */ - handleRemoveUserConfirmationClick_: function(e) { - chrome.send('removeUser', [this.user.profilePath]); - }, - }; - - /** - * Creates a new pod row element. - * @constructor - * @extends {HTMLDivElement} - */ - var PodRow = cr.ui.define('podrow'); - - PodRow.prototype = { - __proto__: HTMLDivElement.prototype, - - // Whether this user pod row is shown for the first time. - firstShown_: true, - - // True if inside focusPod(). - insideFocusPod_: false, - - // Focused pod. - focusedPod_: undefined, - - // Activated pod, i.e. the pod of current login attempt. - activatedPod_: undefined, - - // Pod that was most recently focused, if any. - lastFocusedPod_: undefined, - - // Note: created only in decorate() ! - wallpaperLoader_: undefined, - - // Pods whose initial images haven't been loaded yet. - podsWithPendingImages_: [], - - /** @override */ - decorate: function() { - // Event listeners that are installed for the time period during which - // the element is visible. - this.listeners_ = { - focus: [this.handleFocus_.bind(this), true /* useCapture */], - click: [this.handleClick_.bind(this), true], - mousemove: [this.handleMouseMove_.bind(this), false], - keydown: [this.handleKeyDown.bind(this), false] - }; - this.wallpaperLoader_ = new login.WallpaperLoader(); - }, - - /** - * Returns all the pods in this pod row. - * @type {NodeList} - */ - get pods() { - return Array.prototype.slice.call(this.children); - }, - - /** - * Return true if user pod row has only single user pod in it. - * @type {boolean} - */ - get isSinglePod() { - return this.children.length == 1; - }, - - /** - * Returns pod with the given username (null if there is no such pod). - * @param {string} username Username to be matched. - * @return {Object} Pod with the given username. null if pod hasn't been - * found. - */ - getPodWithUsername_: function(username) { - for (var i = 0, pod; pod = this.pods[i]; ++i) { - if (pod.user.username == username) - return pod; - } - return null; - }, - - /** - * True if the the pod row 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; - } - }, - - /** - * Creates a user pod from given email. - * @param {string} email User's email. - */ - createUserPod: function(user) { - var userPod; - if (user.isDesktopUser) - userPod = new DesktopUserPod({user: user}); - else if (user.publicAccount) - userPod = new PublicAccountUserPod({user: user}); - else - userPod = new UserPod({user: user}); - - userPod.hidden = false; - return userPod; - }, - - /** - * Add an existing user pod to this pod row. - * @param {!Object} user User info dictionary. - * @param {boolean} animated Whether to use init animation. - */ - addUserPod: function(user, animated) { - var userPod = this.createUserPod(user); - if (animated) { - userPod.classList.add('init'); - userPod.nameElement.classList.add('init'); - } - - this.appendChild(userPod); - userPod.initialize(); - }, - - /** - * Removes user pod from pod row. - * @param {string} email User's email. - */ - removeUserPod: function(username) { - var podToRemove = this.getPodWithUsername_(username); - if (podToRemove == null) { - console.warn('Attempt to remove not existing pod for ' + username + - '.'); - return; - } - this.removeChild(podToRemove); - this.placePods_(); - }, - - /** - * Returns index of given pod or -1 if not found. - * @param {UserPod} pod Pod to look up. - * @private - */ - indexOf_: function(pod) { - for (var i = 0; i < this.pods.length; ++i) { - if (pod == this.pods[i]) - return i; - } - return -1; - }, - - /** - * Start first time show animation. - */ - startInitAnimation: function() { - // Schedule init animation. - for (var i = 0, pod; pod = this.pods[i]; ++i) { - window.setTimeout(removeClass, 500 + i * 70, pod, 'init'); - window.setTimeout(removeClass, 700 + i * 70, pod.nameElement, 'init'); - } - }, - - /** - * Start login success animation. - */ - startAuthenticatedAnimation: function() { - var activated = this.indexOf_(this.activatedPod_); - if (activated == -1) - return; - - for (var i = 0, pod; pod = this.pods[i]; ++i) { - if (i < activated) - pod.classList.add('left'); - else if (i > activated) - pod.classList.add('right'); - else - pod.classList.add('zoom'); - } - }, - - /** - * Populates pod row with given existing users and start init animation. - * @param {array} users Array of existing user emails. - * @param {boolean} animated Whether to use init animation. - */ - loadPods: function(users, animated) { - // Clear existing pods. - this.innerHTML = ''; - this.focusedPod_ = undefined; - this.activatedPod_ = undefined; - this.lastFocusedPod_ = undefined; - - // Switch off animation - Oobe.getInstance().toggleClass('flying-pods', false); - - // Populate the pod row. - for (var i = 0; i < users.length; ++i) { - this.addUserPod(users[i], animated); - } - for (var i = 0, pod; pod = this.pods[i]; ++i) { - this.podsWithPendingImages_.push(pod); - } - // Make sure we eventually show the pod row, even if some image is stuck. - setTimeout(function() { - $('pod-row').classList.remove('images-loading'); - }, POD_ROW_IMAGES_LOAD_TIMEOUT_MS); - - this.placePods_(); - - // Without timeout changes in pods positions will be animated even though - // it happened when 'flying-pods' class was disabled. - setTimeout(function() { - Oobe.getInstance().toggleClass('flying-pods', true); - }, 0); - - this.focusPod(this.preselectedPod); - }, - - /** - * Shows a button on a user pod with an icon. Clicking on this button - * triggers an event used by the chrome.screenlockPrivate API. - * @param {string} username Username of pod to add button - * @param {string} iconURL URL of the button icon - */ - showUserPodButton: function(username, iconURL) { - var pod = this.getPodWithUsername_(username); - if (pod == null) { - console.error('Unable to show user pod button for ' + username + - ': user pod not found.'); - return; - } - - pod.customButton.hidden = false; - var icon = - pod.customButton.querySelector('.custom-button-icon'); - icon.src = iconURL; - }, - - /** - * Called when window was resized. - */ - onWindowResize: function() { - var layout = this.calculateLayout_(); - if (layout.columns != this.columns || layout.rows != this.rows) - this.placePods_(); - }, - - /** - * Returns width of podrow having |columns| number of columns. - * @private - */ - columnsToWidth_: function(columns) { - var margin = MARGIN_BY_COLUMNS[columns]; - return 2 * POD_ROW_PADDING + columns * POD_WIDTH + (columns - 1) * margin; - }, - - /** - * Returns height of podrow having |rows| number of rows. - * @private - */ - rowsToHeight_: function(rows) { - return 2 * POD_ROW_PADDING + rows * POD_HEIGHT; - }, - - /** - * Calculates number of columns and rows that podrow should have in order to - * hold as much its pods as possible for current screen size. Also it tries - * to choose layout that looks good. - * @return {{columns: number, rows: number}} - */ - calculateLayout_: function() { - var preferredColumns = this.pods.length < COLUMNS.length ? - COLUMNS[this.pods.length] : COLUMNS[COLUMNS.length - 1]; - var maxWidth = Oobe.getInstance().clientAreaSize.width; - var columns = preferredColumns; - while (maxWidth < this.columnsToWidth_(columns) && columns > 1) - --columns; - var rows = Math.floor((this.pods.length - 1) / columns) + 1; - var maxHeigth = Oobe.getInstance().clientAreaSize.height; - while (maxHeigth < this.rowsToHeight_(rows) && rows > 1) - --rows; - // One more iteration if it's not enough cells to place all pods. - while (maxWidth >= this.columnsToWidth_(columns + 1) && - columns * rows < this.pods.length && - columns < MAX_NUMBER_OF_COLUMNS) { - ++columns; - } - return {columns: columns, rows: rows}; - }, - - /** - * Places pods onto their positions onto pod grid. - * @private - */ - placePods_: function() { - var layout = this.calculateLayout_(); - var columns = this.columns = layout.columns; - var rows = this.rows = layout.rows; - var maxPodsNumber = columns * rows; - var margin = MARGIN_BY_COLUMNS[columns]; - this.parentNode.setPreferredSize( - this.columnsToWidth_(columns), this.rowsToHeight_(rows)); - this.pods.forEach(function(pod, index) { - if (pod.offsetHeight != POD_HEIGHT) - console.error('Pod offsetHeight and POD_HEIGHT are not equal.'); - if (pod.offsetWidth != POD_WIDTH) - console.error('Pod offsetWidht and POD_WIDTH are not equal.'); - if (index >= maxPodsNumber) { - pod.hidden = true; - return; - } - pod.hidden = false; - var column = index % columns; - var row = Math.floor(index / columns); - pod.left = POD_ROW_PADDING + column * (POD_WIDTH + margin); - pod.top = POD_ROW_PADDING + row * POD_HEIGHT; - }); - Oobe.getInstance().updateScreenSize(this.parentNode); - }, - - /** - * Number of columns. - * @type {?number} - */ - set columns(columns) { - // Cannot use 'columns' here. - this.setAttribute('ncolumns', columns); - }, - get columns() { - return this.getAttribute('ncolumns'); - }, - - /** - * Number of rows. - * @type {?number} - */ - set rows(rows) { - // Cannot use 'rows' here. - this.setAttribute('nrows', rows); - }, - get rows() { - return this.getAttribute('nrows'); - }, - - /** - * Whether the pod is currently focused. - * @param {UserPod} pod Pod to check for focus. - * @return {boolean} Pod focus status. - */ - isFocused: function(pod) { - return this.focusedPod_ == pod; - }, - - /** - * Focuses a given user pod or clear focus when given null. - * @param {UserPod=} podToFocus User pod to focus (undefined clears focus). - * @param {boolean=} opt_force If true, forces focus update even when - * podToFocus is already focused. - */ - focusPod: function(podToFocus, opt_force) { - if (this.isFocused(podToFocus) && !opt_force) { - this.keyboardActivated_ = false; - return; - } - - // Make sure there's only one focusPod operation happening at a time. - if (this.insideFocusPod_) { - this.keyboardActivated_ = false; - return; - } - this.insideFocusPod_ = true; - - this.wallpaperLoader_.reset(); - for (var i = 0, pod; pod = this.pods[i]; ++i) { - if (!this.isSinglePod) { - pod.isActionBoxMenuActive = false; - } - if (pod != podToFocus) { - pod.isActionBoxMenuHovered = false; - pod.classList.remove('focused'); - pod.classList.remove('faded'); - pod.reset(false); - } - } - - // Clear any error messages for previous pod. - if (!this.isFocused(podToFocus)) - Oobe.clearErrors(); - - var hadFocus = !!this.focusedPod_; - this.focusedPod_ = podToFocus; - if (podToFocus) { - podToFocus.classList.remove('faded'); - podToFocus.classList.add('focused'); - podToFocus.reset(true); // Reset and give focus. - chrome.send('focusPod', [podToFocus.user.username]); - - this.wallpaperLoader_.scheduleLoad(podToFocus.user.username, opt_force); - this.firstShown_ = false; - this.lastFocusedPod_ = podToFocus; - } - this.insideFocusPod_ = false; - this.keyboardActivated_ = false; - }, - - /** - * Focuses a given user pod by index or clear focus when given null. - * @param {int=} podToFocus index of User pod to focus. - * @param {boolean=} opt_force If true, forces focus update even when - * podToFocus is already focused. - */ - focusPodByIndex: function(podToFocus, opt_force) { - if (podToFocus < this.pods.length) - this.focusPod(this.pods[podToFocus], opt_force); - }, - - /** - * Resets wallpaper to the last active user's wallpaper, if any. - */ - loadLastWallpaper: function() { - if (this.lastFocusedPod_) - this.wallpaperLoader_.scheduleLoad(this.lastFocusedPod_.user.username, - true /* force */); - }, - - /** - * Handles 'onWallpaperLoaded' event. Recalculates statistics and - * [re]schedules next wallpaper load. - */ - onWallpaperLoaded: function(username) { - this.wallpaperLoader_.onWallpaperLoaded(username); - }, - - /** - * Returns the currently activated pod. - * @type {UserPod} - */ - get activatedPod() { - return this.activatedPod_; - }, - set activatedPod(pod) { - if (pod && pod.activate()) - this.activatedPod_ = pod; - }, - - /** - * The pod of the signed-in user, if any; null otherwise. - * @type {?UserPod} - */ - get lockedPod() { - for (var i = 0, pod; pod = this.pods[i]; ++i) { - if (pod.user.signedIn) - return pod; - } - return null; - }, - - /** - * The pod that is preselected on user pod row show. - * @type {?UserPod} - */ - get preselectedPod() { - var lockedPod = this.lockedPod; - var preselectedPod = PRESELECT_FIRST_POD ? - lockedPod || this.pods[0] : lockedPod; - return preselectedPod; - }, - - /** - * Resets input UI. - * @param {boolean} takeFocus True to take focus. - */ - reset: function(takeFocus) { - this.disabled = false; - if (this.activatedPod_) - this.activatedPod_.reset(takeFocus); - }, - - /** - * Restores input focus to current selected pod, if there is any. - */ - refocusCurrentPod: function() { - if (this.focusedPod_) { - this.focusedPod_.focusInput(); - } - }, - - /** - * Clears focused pod password field. - */ - clearFocusedPod: function() { - if (!this.disabled && this.focusedPod_) - this.focusedPod_.reset(true); - }, - - /** - * Shows signin UI. - * @param {string} email Email for signin UI. - */ - showSigninUI: function(email) { - // Clear any error messages that might still be around. - Oobe.clearErrors(); - this.disabled = true; - this.lastFocusedPod_ = this.getPodWithUsername_(email); - Oobe.showSigninUI(email); - }, - - /** - * Updates current image of a user. - * @param {string} username User for which to update the image. - */ - updateUserImage: function(username) { - var pod = this.getPodWithUsername_(username); - if (pod) - pod.updateUserImage(); - }, - - /** - * Resets OAuth token status (invalidates it). - * @param {string} username User for which to reset the status. - */ - resetUserOAuthTokenStatus: function(username) { - var pod = this.getPodWithUsername_(username); - if (pod) { - pod.user.oauthTokenStatus = OAuthTokenStatus.INVALID_OLD; - pod.update(); - } else { - console.log('Failed to update Gaia state for: ' + username); - } - }, - - /** - * Handler of click event. - * @param {Event} e Click Event object. - * @private - */ - handleClick_: function(e) { - if (this.disabled) - return; - - // Clear all menus if the click is outside pod menu and its - // button area. - if (!findAncestorByClass(e.target, 'action-box-menu') && - !findAncestorByClass(e.target, 'action-box-area')) { - for (var i = 0, pod; pod = this.pods[i]; ++i) - pod.isActionBoxMenuActive = false; - } - - // Clears focus if not clicked on a pod and if there's more than one pod. - var pod = findAncestorByClass(e.target, 'pod'); - if ((!pod || pod.parentNode != this) && !this.isSinglePod) { - this.focusPod(); - } - - if (pod) - pod.isActionBoxMenuHovered = true; - - // Return focus back to single pod. - if (this.isSinglePod) { - this.focusPod(this.focusedPod_, true /* force */); - if (!pod) - this.focusedPod_.isActionBoxMenuHovered = false; - } - - // Also stop event propagation. - if (pod && e.target == pod.imageElement) - e.stopPropagation(); - }, - - /** - * Handler of mouse move event. - * @param {Event} e Click Event object. - * @private - */ - handleMouseMove_: function(e) { - if (this.disabled) - return; - if (e.webkitMovementX == 0 && e.webkitMovementY == 0) - return; - - // Defocus (thus hide) action box, if it is focused on a user pod - // and the pointer is not hovering over it. - var pod = findAncestorByClass(e.target, 'pod'); - if (document.activeElement && - document.activeElement.parentNode != pod && - document.activeElement.classList.contains('action-box-area')) { - document.activeElement.parentNode.focus(); - } - - if (pod) - pod.isActionBoxMenuHovered = true; - - // Hide action boxes on other user pods. - for (var i = 0, p; p = this.pods[i]; ++i) - if (p != pod && !p.isActionBoxMenuActive) - p.isActionBoxMenuHovered = false; - }, - - /** - * Handles focus event. - * @param {Event} e Focus Event object. - * @private - */ - handleFocus_: function(e) { - if (this.disabled) - return; - if (e.target.parentNode == this) { - // Focus on a pod - if (e.target.classList.contains('focused')) - e.target.focusInput(); - else - this.focusPod(e.target); - return; - } - - var pod = findAncestorByClass(e.target, 'pod'); - if (pod && pod.parentNode == this) { - // Focus on a control of a pod but not on the action area button. - if (!pod.classList.contains('focused') && - !e.target.classList.contains('action-box-button')) { - this.focusPod(pod); - e.target.focus(); - } - return; - } - - // Clears pod focus when we reach here. It means new focus is neither - // on a pod nor on a button/input for a pod. - // Do not "defocus" user pod when it is a single pod. - // That means that 'focused' class will not be removed and - // input field/button will always be visible. - if (!this.isSinglePod) - this.focusPod(); - }, - - /** - * Handler of keydown event. - * @param {Event} e KeyDown Event object. - */ - handleKeyDown: function(e) { - if (this.disabled) - return; - var editing = e.target.tagName == 'INPUT' && e.target.value; - switch (e.keyIdentifier) { - case 'Left': - if (!editing) { - this.keyboardActivated_ = true; - if (this.focusedPod_ && this.focusedPod_.previousElementSibling) - this.focusPod(this.focusedPod_.previousElementSibling); - else - this.focusPod(this.lastElementChild); - - e.stopPropagation(); - } - break; - case 'Right': - if (!editing) { - this.keyboardActivated_ = true; - if (this.focusedPod_ && this.focusedPod_.nextElementSibling) - this.focusPod(this.focusedPod_.nextElementSibling); - else - this.focusPod(this.firstElementChild); - - e.stopPropagation(); - } - break; - case 'Enter': - if (this.focusedPod_) { - this.activatedPod = this.focusedPod_; - e.stopPropagation(); - } - break; - case 'U+001B': // Esc - if (!this.isSinglePod) - this.focusPod(); - break; - } - }, - - /** - * Called right after the pod row is shown. - */ - handleAfterShow: function() { - // Without timeout changes in pods positions will be animated even though - // it happened when 'flying-pods' class was disabled. - setTimeout(function() { - Oobe.getInstance().toggleClass('flying-pods', true); - }, 0); - // Force input focus for user pod on show and once transition ends. - if (this.focusedPod_) { - var focusedPod = this.focusedPod_; - var screen = this.parentNode; - var self = this; - focusedPod.addEventListener('webkitTransitionEnd', function f(e) { - if (e.target == focusedPod) { - focusedPod.removeEventListener('webkitTransitionEnd', f); - focusedPod.reset(true); - // Notify screen that it is ready. - screen.onShow(); - self.wallpaperLoader_.scheduleLoad(focusedPod.user.username, - true /* force */); - } - }); - // Guard timer for 1 second -- it would conver all possible animations. - ensureTransitionEndEvent(focusedPod, 1000); - } - }, - - /** - * Called right before the pod row is shown. - */ - handleBeforeShow: function() { - Oobe.getInstance().toggleClass('flying-pods', false); - for (var event in this.listeners_) { - this.ownerDocument.addEventListener( - event, this.listeners_[event][0], this.listeners_[event][1]); - } - $('login-header-bar').buttonsTabIndex = UserPodTabOrder.HEADER_BAR; - }, - - /** - * Called when the element is hidden. - */ - handleHide: function() { - for (var event in this.listeners_) { - this.ownerDocument.removeEventListener( - event, this.listeners_[event][0], this.listeners_[event][1]); - } - $('login-header-bar').buttonsTabIndex = 0; - }, - - /** - * Called when a pod's user image finishes loading. - */ - handlePodImageLoad: function(pod) { - var index = this.podsWithPendingImages_.indexOf(pod); - if (index == -1) { - return; - } - - this.podsWithPendingImages_.splice(index, 1); - if (this.podsWithPendingImages_.length == 0) { - this.classList.remove('images-loading'); - } - } - }; - - return { - PodRow: PodRow - }; -}); |