OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview User pod row implementation. | 6 * @fileoverview User pod row implementation. |
7 */ | 7 */ |
8 | 8 |
9 cr.define('login', function() { | 9 cr.define('login', function() { |
10 /** | 10 /** |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 * @const | 78 * @const |
79 */ | 79 */ |
80 var HELP_TOPIC_PUBLIC_SESSION = 3041033; | 80 var HELP_TOPIC_PUBLIC_SESSION = 3041033; |
81 | 81 |
82 /** | 82 /** |
83 * Tab order for user pods. Update these when adding new controls. | 83 * Tab order for user pods. Update these when adding new controls. |
84 * @enum {number} | 84 * @enum {number} |
85 * @const | 85 * @const |
86 */ | 86 */ |
87 var UserPodTabOrder = { | 87 var UserPodTabOrder = { |
88 POD_INPUT: 1, // Password input fields (and whole pods themselves). | 88 POD_INPUT: 1, // Password input fields (and whole pods themselves). |
89 HEADER_BAR: 2, // Buttons on the header bar (Shutdown, Add User). | 89 POD_CUSTOM_ICON: 2, // Pod custom icon next to passwrod input field. |
90 ACTION_BOX: 3, // Action box buttons. | 90 HEADER_BAR: 3, // Buttons on the header bar (Shutdown, Add User). |
91 PAD_MENU_ITEM: 4 // User pad menu items (Remove this user). | 91 ACTION_BOX: 4, // Action box buttons. |
| 92 PAD_MENU_ITEM: 5 // User pad menu items (Remove this user). |
92 }; | 93 }; |
93 | 94 |
94 /** | 95 /** |
95 * Supported authentication types. Keep in sync with the enum in | 96 * Supported authentication types. Keep in sync with the enum in |
96 * chrome/browser/signin/screenlock_bridge.h | 97 * chrome/browser/signin/screenlock_bridge.h |
97 * @enum {number} | 98 * @enum {number} |
98 * @const | 99 * @const |
99 */ | 100 */ |
100 var AUTH_TYPE = { | 101 var AUTH_TYPE = { |
101 OFFLINE_PASSWORD: 0, | 102 OFFLINE_PASSWORD: 0, |
102 ONLINE_SIGN_IN: 1, | 103 ONLINE_SIGN_IN: 1, |
103 NUMERIC_PIN: 2, | 104 NUMERIC_PIN: 2, |
104 USER_CLICK: 3, | 105 USER_CLICK: 3, |
105 EXPAND_THEN_USER_CLICK: 4, | 106 EXPAND_THEN_USER_CLICK: 4, |
| 107 FORCE_OFFLINE_PASSWORD: 5 |
106 }; | 108 }; |
107 | 109 |
108 /** | 110 /** |
109 * Names of authentication types. | 111 * Names of authentication types. |
110 */ | 112 */ |
111 var AUTH_TYPE_NAMES = { | 113 var AUTH_TYPE_NAMES = { |
112 0: 'offlinePassword', | 114 0: 'offlinePassword', |
113 1: 'onlineSignIn', | 115 1: 'onlineSignIn', |
114 2: 'numericPin', | 116 2: 'numericPin', |
115 3: 'userClick', | 117 3: 'userClick', |
116 4: 'expandThenUserClick', | 118 4: 'expandThenUserClick', |
| 119 5: 'forceOfflinePassword' |
117 }; | 120 }; |
118 | 121 |
119 // Focus and tab order are organized as follows: | 122 // Focus and tab order are organized as follows: |
120 // | 123 // |
121 // (1) all user pods have tab index 1 so they are traversed first; | 124 // (1) all user pods have tab index 1 so they are traversed first; |
122 // (2) when a user pod is activated, its tab index is set to -1 and its | 125 // (2) when a user pod is activated, its tab index is set to -1 and its |
123 // main input field gets focus and tab index 1; | 126 // main input field gets focus and tab index 1; |
124 // (3) buttons on the header bar have tab index 2 so they follow user pods; | 127 // (3) if user pod custom icon is interactive, it has tab index 2 so it |
125 // (4) Action box buttons have tab index 3 and follow header bar buttons; | 128 // follows the input. |
126 // (5) lastly, focus jumps to the Status Area and back to user pods. | 129 // (4) buttons on the header bar have tab index 3 so they follow the custom |
| 130 // icon, or user pod if custom icon is not interactive; |
| 131 // (5) Action box buttons have tab index 4 and follow header bar buttons; |
| 132 // (6) lastly, focus jumps to the Status Area and back to user pods. |
127 // | 133 // |
128 // 'Focus' event is handled by a capture handler for the whole document | 134 // 'Focus' event is handled by a capture handler for the whole document |
129 // and in some cases 'mousedown' event handlers are used instead of 'click' | 135 // and in some cases 'mousedown' event handlers are used instead of 'click' |
130 // handlers where it's necessary to prevent 'focus' event from being fired. | 136 // handlers where it's necessary to prevent 'focus' event from being fired. |
131 | 137 |
132 /** | 138 /** |
133 * Helper function to remove a class from given element. | 139 * Helper function to remove a class from given element. |
134 * @param {!HTMLElement} el Element whose class list to change. | 140 * @param {!HTMLElement} el Element whose class list to change. |
135 * @param {string} cl Class to remove. | 141 * @param {string} cl Class to remove. |
136 */ | 142 */ |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 animationInterval_: null, | 251 animationInterval_: null, |
246 | 252 |
247 /** | 253 /** |
248 * The width of the resource that contains representations for different | 254 * The width of the resource that contains representations for different |
249 * animation stages. | 255 * animation stages. |
250 * @type {number} | 256 * @type {number} |
251 * @private | 257 * @private |
252 */ | 258 */ |
253 animationResourceSize_: 0, | 259 animationResourceSize_: 0, |
254 | 260 |
| 261 /** |
| 262 * When {@code fadeOut} is called, the element gets hidden using fadeout |
| 263 * animation. This is reference to the listener for transition end added to |
| 264 * the icon element while it's fading out. |
| 265 * @type {?function(Event)} |
| 266 * @private |
| 267 */ |
| 268 hideTransitionListener_: null, |
| 269 |
| 270 /** |
| 271 * Callback for click and 'Enter' key events that gets set if the icon is |
| 272 * interactive. |
| 273 * @type {?function()} |
| 274 * @private |
| 275 */ |
| 276 actionHandler_: null, |
| 277 |
255 /** @override */ | 278 /** @override */ |
256 decorate: function() { | 279 decorate: function() { |
257 this.iconElement.addEventListener('mouseover', | 280 this.iconElement.addEventListener('mouseover', |
258 this.showTooltipSoon_.bind(this)); | 281 this.showTooltipSoon_.bind(this)); |
259 this.iconElement.addEventListener('mouseout', | 282 this.iconElement.addEventListener('mouseout', |
260 this.hideTooltip_.bind(this, false)); | 283 this.hideTooltip_.bind(this, false)); |
261 this.iconElement.addEventListener('mousedown', | 284 this.iconElement.addEventListener('mousedown', |
262 this.hideTooltip_.bind(this, false)); | 285 this.hideTooltip_.bind(this, false)); |
| 286 this.iconElement.addEventListener('click', |
| 287 this.handleClick_.bind(this)); |
| 288 this.iconElement.addEventListener('keydown', |
| 289 this.handleKeyDown_.bind(this)); |
| 290 |
| 291 // When the icon is focused using mouse, there should be no outline shown. |
| 292 // Preventing default mousedown event accomplishes this. |
| 293 this.iconElement.addEventListener('mousedown', function(e) { |
| 294 e.preventDefault(); |
| 295 }); |
263 }, | 296 }, |
264 | 297 |
265 /** | 298 /** |
266 * Getter for the icon element's div. | 299 * Getter for the icon element's div. |
267 * @return {HTMLDivElement} | 300 * @return {HTMLDivElement} |
268 */ | 301 */ |
269 get iconElement() { | 302 get iconElement() { |
270 return this.querySelector('.custom-icon'); | 303 return this.querySelector('.custom-icon'); |
271 }, | 304 }, |
272 | 305 |
(...skipping 18 matching lines...) Expand all Loading... |
291 this.iconElement.style.backgroundImage = | 324 this.iconElement.style.backgroundImage = |
292 '-webkit-image-set(' + | 325 '-webkit-image-set(' + |
293 'url(' + iconUrl + '@1x) 1x,' + | 326 'url(' + iconUrl + '@1x) 1x,' + |
294 'url(' + iconUrl + '@2x) 2x)'; | 327 'url(' + iconUrl + '@2x) 2x)'; |
295 }, | 328 }, |
296 | 329 |
297 /** | 330 /** |
298 * Shows the icon. | 331 * Shows the icon. |
299 */ | 332 */ |
300 show: function() { | 333 show: function() { |
| 334 this.resetHideTransitionState_(); |
301 this.hidden = false; | 335 this.hidden = false; |
302 }, | 336 }, |
303 | 337 |
304 /** | 338 /** |
305 * Hides the icon. Makes sure the tooltip is hidden and animation reset. | 339 * Hides the icon using a fade-out animation. |
306 */ | 340 */ |
307 hide: function() { | 341 fadeOut: function() { |
| 342 // The icon is already being hidden. |
| 343 if (this.iconElement.classList.contains('faded')) |
| 344 return; |
| 345 |
308 this.hideTooltip_(true); | 346 this.hideTooltip_(true); |
309 this.setAnimation(null); | 347 this.iconElement.classList.add('faded'); |
310 this.hidden = true; | 348 this.hideTransitionListener_ = this.hide_.bind(this); |
| 349 this.iconElement.addEventListener('webkitTransitionEnd', |
| 350 this.hideTransitionListener_); |
| 351 ensureTransitionEndEvent(this.iconElement, 200); |
311 }, | 352 }, |
312 | 353 |
313 /** | 354 /** |
314 * Sets the icon size element size. | 355 * Sets the icon size element size. |
315 * @param {!{width: number, height: number}} size The icon size. | 356 * @param {!{width: number, height: number}} size The icon size. |
316 */ | 357 */ |
317 setSize: function(size) { | 358 setSize: function(size) { |
318 this.height_ = size.height < CUSTOM_ICON_CONTAINER_SIZE ? | 359 this.height_ = size.height < CUSTOM_ICON_CONTAINER_SIZE ? |
319 size.height : CUSTOM_ICON_COTAINER_SIZE; | 360 size.height : CUSTOM_ICON_COTAINER_SIZE; |
320 this.iconElement.style.height = this.height_ + 'px'; | 361 this.iconElement.style.height = this.height_ + 'px'; |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 this.iconElement.style.backgroundPosition = 'center'; | 437 this.iconElement.style.backgroundPosition = 'center'; |
397 if (!animation) | 438 if (!animation) |
398 return; | 439 return; |
399 this.lastAnimationOffset_ = 0; | 440 this.lastAnimationOffset_ = 0; |
400 this.animationResourceSize_ = animation.resourceWidth; | 441 this.animationResourceSize_ = animation.resourceWidth; |
401 this.animationInterval_ = setInterval(this.progressAnimation_.bind(this), | 442 this.animationInterval_ = setInterval(this.progressAnimation_.bind(this), |
402 animation.frameLengthMs); | 443 animation.frameLengthMs); |
403 }, | 444 }, |
404 | 445 |
405 /** | 446 /** |
| 447 * Sets up icon tabIndex attribute and handler for click and 'Enter' key |
| 448 * down events. |
| 449 * @param {?function()} callback If icon should be interactive, the |
| 450 * function to get called on click and 'Enter' key down events. Should |
| 451 * be null to make the icon non interactive. |
| 452 */ |
| 453 setInteractive: function(callback) { |
| 454 // Update tabIndex property if needed. |
| 455 if (!!this.actionHandler_ != !!callback) { |
| 456 if (callback) { |
| 457 this.iconElement.setAttribute('tabIndex', |
| 458 UserPodTabOrder.POD_CUSTOM_ICON); |
| 459 } else { |
| 460 this.iconElement.removeAttribute('tabIndex'); |
| 461 } |
| 462 } |
| 463 |
| 464 // Set the new action handler. |
| 465 this.actionHandler_ = callback; |
| 466 }, |
| 467 |
| 468 /** |
| 469 * Hides the icon. Makes sure the tooltip is hidden and animation reset. |
| 470 * @private |
| 471 */ |
| 472 hide_: function() { |
| 473 this.hideTooltip_(true); |
| 474 this.hidden = true; |
| 475 this.setAnimation(null); |
| 476 this.setInteractive(null); |
| 477 this.resetHideTransitionState_(); |
| 478 }, |
| 479 |
| 480 /** |
| 481 * Ensures the icon's transition state potentially set by a call to |
| 482 * {@code fadeOut} is cleared. |
| 483 * @private |
| 484 */ |
| 485 resetHideTransitionState_: function() { |
| 486 if (this.hideTransitionListener_) { |
| 487 this.iconElement.removeEventListener('webkitTransitionEnd', |
| 488 this.hideTransitionListener_); |
| 489 this.hideTransitionListener_ = null; |
| 490 } |
| 491 this.iconElement.classList.toggle('faded', false); |
| 492 }, |
| 493 |
| 494 /** |
| 495 * Handles click event on the icon element. No-op if |
| 496 * {@code this.actionHandler_} is not set. |
| 497 * @param {Event} e The click event. |
| 498 * @private |
| 499 */ |
| 500 handleClick_: function(e) { |
| 501 if (!this.actionHandler_) |
| 502 return; |
| 503 this.actionHandler_(); |
| 504 stopEventPropagation(e); |
| 505 }, |
| 506 |
| 507 /** |
| 508 * Handles key down event on the icon element. Only 'Enter' key is handled. |
| 509 * No-op if {@code this.actionHandler_} is not set. |
| 510 * @param {Event} e The key down event. |
| 511 * @private |
| 512 */ |
| 513 handleKeyDown_: function(e) { |
| 514 if (!this.actionHandler_ || e.keyIdentifier != 'Enter') |
| 515 return; |
| 516 this.actionHandler_(e); |
| 517 stopEventPropagation(e); |
| 518 }, |
| 519 |
| 520 /** |
406 * Called when mouse enters the icon. It sets timeout for showing the | 521 * Called when mouse enters the icon. It sets timeout for showing the |
407 * tooltip. | 522 * tooltip. |
408 * @private | 523 * @private |
409 */ | 524 */ |
410 showTooltipSoon_: function() { | 525 showTooltipSoon_: function() { |
411 if (this.showTooltipTimeout_ || this.tooltipActive_) | 526 if (this.showTooltipTimeout_ || this.tooltipActive_) |
412 return; | 527 return; |
413 this.showTooltipTimeout_ = | 528 this.showTooltipTimeout_ = |
414 setTimeout(this.showTooltip_.bind(this), 1000); | 529 setTimeout(this.showTooltip_.bind(this), 1000); |
415 }, | 530 }, |
416 | 531 |
417 /** | 532 /** |
418 * Shows the current tooltip, if one is set. | 533 * Shows the current tooltip if one is set. |
419 * @private | 534 * @private |
420 */ | 535 */ |
421 showTooltip_: function() { | 536 showTooltip_: function() { |
422 if (this.hidden || !this.tooltip_ || this.tooltipActive_) | 537 if (this.hidden || !this.tooltip_ || this.tooltipActive_) |
423 return; | 538 return; |
424 | 539 |
425 // If autoshown bubble got hidden, clear the autoshown flag. | 540 // If autoshown bubble got hidden, clear the autoshown flag. |
426 if ($('bubble').hidden && this.tooltipAutoshown_) | 541 if ($('bubble').hidden && this.tooltipAutoshown_) |
427 this.tooltipAutoshown_ = false; | 542 this.tooltipAutoshown_ = false; |
428 | 543 |
(...skipping 537 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
966 */ | 1081 */ |
967 get authValue() { | 1082 get authValue() { |
968 return this.authValue_; | 1083 return this.authValue_; |
969 }, | 1084 }, |
970 | 1085 |
971 /** | 1086 /** |
972 * True if the the user pod uses a password to authenticate. | 1087 * True if the the user pod uses a password to authenticate. |
973 * @type {bool} | 1088 * @type {bool} |
974 */ | 1089 */ |
975 get isAuthTypePassword() { | 1090 get isAuthTypePassword() { |
976 return this.authType_ == AUTH_TYPE.OFFLINE_PASSWORD; | 1091 return this.authType_ == AUTH_TYPE.OFFLINE_PASSWORD || |
| 1092 this.authType_ == AUTH_TYPE.FORCE_OFFLINE_PASSWORD; |
977 }, | 1093 }, |
978 | 1094 |
979 /** | 1095 /** |
980 * True if the the user pod uses a user click to authenticate. | 1096 * True if the the user pod uses a user click to authenticate. |
981 * @type {bool} | 1097 * @type {bool} |
982 */ | 1098 */ |
983 get isAuthTypeUserClick() { | 1099 get isAuthTypeUserClick() { |
984 return this.authType_ == AUTH_TYPE.USER_CLICK; | 1100 return this.authType_ == AUTH_TYPE.USER_CLICK; |
985 }, | 1101 }, |
986 | 1102 |
(...skipping 1205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2192 pod.customIconElement.setIconAsResourceUrl(icon.resourceUrl); | 2308 pod.customIconElement.setIconAsResourceUrl(icon.resourceUrl); |
2193 } else if (icon.data) { | 2309 } else if (icon.data) { |
2194 pod.customIconElement.setIconAsImageSet(icon.data); | 2310 pod.customIconElement.setIconAsImageSet(icon.data); |
2195 } else { | 2311 } else { |
2196 return; | 2312 return; |
2197 } | 2313 } |
2198 | 2314 |
2199 pod.customIconElement.setSize(icon.size || {width: 0, height: 0}); | 2315 pod.customIconElement.setSize(icon.size || {width: 0, height: 0}); |
2200 pod.customIconElement.setAnimation(icon.animation || null); | 2316 pod.customIconElement.setAnimation(icon.animation || null); |
2201 pod.customIconElement.setOpacity(icon.opacity || 100); | 2317 pod.customIconElement.setOpacity(icon.opacity || 100); |
| 2318 if (icon.hardlockOnClick) { |
| 2319 pod.customIconElement.setInteractive( |
| 2320 this.hardlockUserPod_.bind(this, username)); |
| 2321 } else { |
| 2322 pod.customIconElement.setInteractive(null); |
| 2323 } |
2202 pod.customIconElement.show(); | 2324 pod.customIconElement.show(); |
2203 // This has to be called after |show| in case the tooltip should be shown | 2325 // This has to be called after |show| in case the tooltip should be shown |
2204 // immediatelly. | 2326 // immediatelly. |
2205 pod.customIconElement.setTooltip( | 2327 pod.customIconElement.setTooltip( |
2206 icon.tooltip || {text: '', autoshow: false}); | 2328 icon.tooltip || {text: '', autoshow: false}); |
2207 }, | 2329 }, |
2208 | 2330 |
2209 /** | 2331 /** |
| 2332 * Hard-locks user pod for the user. If user pod is hard-locked, it can be |
| 2333 * only unlocked using password, and the authentication type cannot be |
| 2334 * changed. |
| 2335 * @param {!string} username The user's username. |
| 2336 * @private |
| 2337 */ |
| 2338 hardlockUserPod_: function(username) { |
| 2339 chrome.send('hardlockPod', [username]); |
| 2340 }, |
| 2341 |
| 2342 /** |
2210 * Hides the custom icon in the user pod added by showUserPodCustomIcon(). | 2343 * Hides the custom icon in the user pod added by showUserPodCustomIcon(). |
2211 * @param {string} username Username of pod to remove button | 2344 * @param {string} username Username of pod to remove button |
2212 */ | 2345 */ |
2213 hideUserPodCustomIcon: function(username) { | 2346 hideUserPodCustomIcon: function(username) { |
2214 var pod = this.getPodWithUsername_(username); | 2347 var pod = this.getPodWithUsername_(username); |
2215 if (pod == null) { | 2348 if (pod == null) { |
2216 console.error('Unable to hide user pod button for ' + username + | 2349 console.error('Unable to hide user pod button for ' + username + |
2217 ': user pod not found.'); | 2350 ': user pod not found.'); |
2218 return; | 2351 return; |
2219 } | 2352 } |
2220 | 2353 |
2221 pod.customIconElement.hide(); | 2354 pod.customIconElement.fadeOut(); |
2222 }, | 2355 }, |
2223 | 2356 |
2224 /** | 2357 /** |
2225 * Sets the authentication type used to authenticate the user. | 2358 * Sets the authentication type used to authenticate the user. |
2226 * @param {string} username Username of selected user | 2359 * @param {string} username Username of selected user |
2227 * @param {number} authType Authentication type, must be one of the | 2360 * @param {number} authType Authentication type, must be one of the |
2228 * values listed in AUTH_TYPE enum. | 2361 * values listed in AUTH_TYPE enum. |
2229 * @param {string} value The initial value to use for authentication. | 2362 * @param {string} value The initial value to use for authentication. |
2230 */ | 2363 */ |
2231 setAuthType: function(username, authType, value) { | 2364 setAuthType: function(username, authType, value) { |
(...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2856 if (pod && pod.multiProfilesPolicyApplied) { | 2989 if (pod && pod.multiProfilesPolicyApplied) { |
2857 pod.userTypeBubbleElement.classList.remove('bubble-shown'); | 2990 pod.userTypeBubbleElement.classList.remove('bubble-shown'); |
2858 } | 2991 } |
2859 } | 2992 } |
2860 }; | 2993 }; |
2861 | 2994 |
2862 return { | 2995 return { |
2863 PodRow: PodRow | 2996 PodRow: PodRow |
2864 }; | 2997 }; |
2865 }); | 2998 }); |
OLD | NEW |