Chromium Code Reviews
[email protected] (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Side by Side Diff: chrome/browser/resources/login.js

Issue 7980012: [ChromeOS] Reland - Make WebUI login use only needed resources. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/resources/login.html ('k') | chrome/browser/resources/login_ui.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview Login Screen
7 * This is the main code for the login screen.
8 */
9
10 /**
11 * Simple common assertion API
12 * @param {*} condition The condition to test. Note that this may be used to
13 * test whether a value is defined or not, and we don't want to force a
14 * cast to Boolean.
15 * @param {string=} opt_message A message to use in any error.
16 */
17 function assert(condition, opt_message) {
18 if (condition)
19 return;
20
21 var msg = 'Assertion failed';
22 if (opt_message)
23 msg = msg + ': ' + opt_message;
24 throw new Error(msg);
25 }
26
27 var LoginScreen = (function() {
28 'use strict';
29
30 function LoginScreen() {
31 }
32
33 LoginScreen.prototype = {
34 /**
35 * The guest card object
36 * @type {Slider|undefined}
37 * private
38 */
39 guestCard_: null,
40
41 /**
42 * The Slider object to use for scrolling through users.
43 * @type {Slider|undefined}
44 */
45 slider_: null,
46
47 /**
48 * Template to use for creating new 'user-card' elements
49 * @type {!Element|undefined}
50 */
51 userCardTemplate_: null,
52
53 /**
54 * Indicates whether the shift key is down.
55 * @type {bool}
56 */
57 isShiftPressed_: false,
58
59 /**
60 * Indicates whether the alt key is down.
61 * @type {bool}
62 */
63 isAltPressed_: false,
64
65 /**
66 * Indicates whether the ctrl key is down.
67 * @type {bool}
68 */
69 isCtrlPressed_: false,
70
71 /**
72 * Holds all event handlers tied to users (and so subject to removal when
73 * the user list is refreshed)
74 * @type {!EventTracker}
75 */
76 userEvents_: null,
77
78 /**
79 * Removes all children of an element which have a given class in
80 * their classList.
81 * @param {!Element} element The parent element to examine.
82 * @param {string} className The class to look for.
83 */
84 removeChildrenByClassName_: function (element, className) {
85 var child = element.firstElementChild;
86 while (child) {
87 var prev = child;
88 child = child.nextElementSibling;
89 if (prev.classList.contains(className))
90 element.removeChild(prev);
91 }
92 },
93
94 /**
95 * Search an elements ancestor chain for the nearest element that is a
96 * member of the specified class.
97 * @param {!Element} element The element to start searching from.
98 * @param {string} className The name of the class to locate.
99 * @return {Element} The first ancestor of the specified class or null.
100 */
101 getParentByClassName_: function(element, className) {
102 for (var e = element; e; e = e.parentElement) {
103 if (e.classList.contains(className))
104 return e;
105 }
106 return null;
107 },
108
109 /**
110 * Create a new user element and attach it to the end of the slider.
111 * @param {!Element} parent The element where the user should be inserted.
112 * @param {!Object} user The user object to user box for.
113 */
114 addUser: function(parent, user, index) {
115 // Make a deep copy of the template and clear its ID
116 var containerElement = this.userCardTemplate_.cloneNode(true);
117 this.userCardTemplate_.id = null;
118
119 var userElement = containerElement.getElementsByClassName('user')[0];
120 assert(userElement, 'Expected user-template to have a user child');
121 assert(typeof(user.name) == 'string',
122 'Expected every user to have a name.');
123
124 userElement.setAttribute('display-name', user.name);
125 userElement.setAttribute('user-name', user.emailAddress);
126 userElement.setAttribute('user-index', index);
127
128 // Find the span element (if any) and fill it in with the app name
129 var span = userElement.querySelector('span');
130 if (span)
131 span.textContent = user.name;
132
133 // Fill in the image
134 var userImg = userElement.getElementsByClassName('user-image')[0];
135 if (userImg) {
136 userImg.style.backgroundImage = "url('" + user.imageUrl + "')";
137 // We put a click handler just on the user image - so clicking on the
138 // margins between users doesn't do anything
139 var self = this;
140 this.userEvents_.add(userImg, 'click', function(e) {
141 self.userClick(e);
142 }, false);
143 }
144 var userLoginBox =
145 userElement.getElementsByClassName('user-login-box')[0];
146 var guestLoginBox =
147 userElement.getElementsByClassName('guest-login-box')[0];
148 var isGuest = !user.email_address;
149 if (!isGuest) {
150 containerElement.addEventListener(Slider.EventType.ACTIVATE,
151 this.userActivateCard);
152 containerElement.addEventListener(Slider.EventType.DEACTIVATE,
153 this.userDeactivateCard);
154
155 var deleteButton =
156 userElement.getElementsByClassName('delete-button')[0];
157 deleteButton.addEventListener('click', this.userDeleteCard);
158
159 userLoginBox.addEventListener('keypress', function(e) {
160 userLoginKeyPress(e, userElement);
161 });
162 } else {
163 this.guestCard_ = containerElement;
164
165 containerElement.addEventListener(Slider.EventType.ACTIVATE,
166 this.guestActivateCard);
167 containerElement.addEventListener(Slider.EventType.DEACTIVATE,
168 this.guestDeactivateCard);
169
170 userLoginBox.hidden = true;
171
172 var loginButton =
173 guestLoginBox.getElementsByClassName('loginbutton')[0];
174 loginButton.addEventListener('click', function(e) {
175 chrome.send('launchIncognito', []);
176 // Don't allow the click to trigger a link or anything
177 e.preventDefault();
178 // Prevent the user-frame from receiving the event.
179 e.stopPropagation();
180 }, true);
181 }
182
183 // Insert at the end of the provided page
184 parent.appendChild(containerElement);
185 // Display the container element
186 containerElement.hidden = false;
187 },
188
189 /**
190 * Invoked when a user is clicked
191 * @param {Event} e The click event.
192 */
193 userClick: function(e) {
194 var target = e.currentTarget;
195 var user = this.getParentByClassName_(target, 'user');
196 assert(user, 'userClick should have been on a descendant of a user');
197
198 var userIndex = user.getAttribute('user-index');
199 assert(userIndex, 'unexpected user without userIndex');
200
201 if (this.slider.currentCardIndex == null) {
202 this.slider.currentCardIndex = userIndex;
203 } else {
204 // get out of user selected mode.
205 this.slider.currentCardIndex = null;
206 }
207
208 // Don't allow the click to trigger a link or anything
209 e.preventDefault();
210 // Prevent the user-frame from receiving the event.
211 e.stopPropagation();
212 },
213
214 /**
215 * Invoked when the selected user card changes
216 * @param {Event} e The selection changed event.
217 */
218 userFrameSelectionChanged: function(e) {
219 var userCards = $('user-list').getElementsByClassName('user-card');
220 for (var i = 0; i < userCards.length; i++) {
221 if (e.activeCardIndex == null || i == e.activeCardIndex) {
222 userCards[i].classList.remove('user-card-grayed');
223 } else {
224 userCards[i].classList.add('user-card-grayed');
225 }
226 }
227 },
228
229 /**
230 * Invoked when the guest card is activated.
231 * @param {Event} e The Activate Card event
232 */
233 guestActivateCard: function(e) {
234 var card = e.target;
235 var user = card.getElementsByClassName('user')[0];
236
237 // Focus the card.
238 user.classList.add('focus');
239
240 assert(user, 'user element not found');
241
242 // Hide the guest box.
243 var guestBox = user.querySelector('span');
244 guestBox.hidden = true;
245
246 // This is a guest.
247 var guestLoginBox = user.getElementsByClassName('guest-login-box')[0];
248 assert(guestLoginBox, 'guest login box not found');
249 // Show the guest login box.
250 guestLoginBox.hidden = false;
251 },
252
253 /**
254 * Invoked when the guest card is deactivated.
255 * @param {Event} e The Deactivate Card event
256 */
257 guestDeactivateCard: function(e) {
258 var card = e.target;
259 var user = card.getElementsByClassName('user')[0];
260 assert(user, 'user element not found');
261 // Show the guest box.
262 var guestBox = user.querySelector('span');
263 guestBox.hidden = false;
264 // Unfocus the card.
265 user.classList.remove('focus');
266 // Hide the guest login box
267 var guestLoginBox = user.getElementsByClassName('guest-login-box')[0];
268 assert(guestLoginBox, 'guest login box not found');
269 guestLoginBox.hidden = true;
270 },
271
272 /**
273 * Invoked when the user card delete button is clicked
274 * @param {Event} e The Click event.
275 */
276 userDeleteCard: function(e) {
277 var deleteButton = e.target;
278 var userImageBorderBox = deleteButton.parentNode;
279 var userElement = userImageBorderBox.parentNode;
280 var userCard = userElement.parentNode;
281 $('user-list').removeChild(userCard);
282
283 // Update the user indicies
284 var usersOnList = $('user-list').getElementsByClassName('user-card');
285 for (var userIndex = 0; userIndex < usersOnList.length; userIndex++) {
286 var uElement = usersOnList[userIndex].getElementsByClassName('user')[0];
287 uElement.setAttribute('user-index', userIndex);
288 }
289 this.updateSliderCards();
290
291 chrome.send('removeUser', [getUserName(userElement)]);
292 },
293
294 /**
295 * Invoked when the user card is activated.
296 * @param {Event} e The Activate Card event
297 */
298 userActivateCard: function(e) {
299 var card = e.target;
300 var user = card.getElementsByClassName('user')[0];
301 assert(user, 'user element not found');
302
303 // Focus the card.
304 user.classList.add('focus');
305
306 // Hide the email box.
307 var emailBox = user.querySelector('span');
308 emailBox.hidden = true;
309
310 // Show the login box
311 var userLoginBox = user.getElementsByClassName('user-login-box')[0];
312 assert(userLoginBox, 'user login box not found');
313 userLoginBox.hidden = false;
314
315 // Clear and focus the password box.
316 var passwordBox = userLoginBox.getElementsByClassName('passwordbox')[0];
317 assert(passwordBox, 'passwordBox element not found');
318 passwordBox.value = '';
319 passwordBox.focus();
320
321 // Show the delete button.
322 var deleteButton = user.getElementsByClassName('delete-button')[0];
323 deleteButton.hidden = false;
324 },
325
326 /**
327 * Invoked when the user card is deactivated.
328 * @param {Event} e The Deactivate Card event
329 */
330 userDeactivateCard: function(e) {
331 var card = e.target;
332 var user = card.getElementsByClassName('user')[0];
333 assert(user, 'user element not found');
334
335 // Unfocus the card.
336 user.classList.remove('focus');
337
338 // Hide the user login box
339 var userLoginBox = user.getElementsByClassName('user-login-box')[0];
340 assert(userLoginBox, 'user login box not found');
341 userLoginBox.hidden = true;
342
343 // Show the email box.
344 var emailBox = user.querySelector('span');
345 emailBox.hidden = false;
346
347 // Hide the delete button.
348 var deleteButton = user.getElementsByClassName('delete-button')[0];
349 deleteButton.hidden = true;
350 },
351
352 /**
353 * Invoked whenever the users in user-list have changed so that
354 * the Slider knows about the new elements.
355 */
356 updateSliderCards: function() {
357 var userCards = $('user-list').getElementsByClassName('user-card');
358 var cardArray = [];
359 for (var i = 0; i < userCards.length; i++)
360 cardArray[i] = userCards[i];
361 this.slider.setCards(cardArray, 0);
362 },
363
364 getUsersCallback: function(users) {
365 this.userEvents_.removeAll();
366 // grab the add-user-card before we remove it from the list.
367 var addUserCard = $('add-user-card');
368 // remove all user cards
369 this.removeChildrenByClassName_($('user-list'), 'user-card');
370
371 // Add the "Add User" card to the user list.
372 $('user-list').appendChild(addUserCard);
373 // Add the users to the user list.
374 for (var i = 0; i < users.length; i++) {
375 var user = users[i];
376 this.addUser($('user-list'), user, i + 1);
377 }
378
379 // Tell the slider about the pages
380 this.updateSliderCards();
381
382 var dummy = $('user-frame').offsetWidth;
383 $('user-frame').style.visibility = 'visible';
384 },
385
386 /**
387 * Returns the 'display-name' attribute of a 'user' element.
388 * @param {!Element} element The 'user' element.
389 */
390 getDisplayName: function(userElement) {
391 // TODO: verify type of user element
392 return userElement.getAttribute('display-name');
393 },
394
395 /**
396 * Returns the 'user-name' attribute of a 'user' element.
397 * @param {!Element} element The 'user' element.
398 */
399 getUserName: function(userElement) {
400 // TODO: verify type of user element
401 return userElement.getAttribute('user-name');
402 },
403
404 /**
405 * Handles keyboard navigation of the login screen.
406 * @param {!Event} element The keypress event.
407 */
408 userLoginKeyPress: function(e, userElement) {
409 if (e.keyCode == 13) {
410 e.preventDefault();
411 assert(userElement.classList.contains('user'),
412 'The parent element is not a user element.');
413
414 var userImg = userElement.getElementsByClassName('user-image')[0];
415 var userLoginBox =
416 userElement.getElementsByClassName('user-login-box')[0];
417 var passwordBox = userLoginBox.getElementsByClassName('passwordbox')[0];
418
419 var userName = getUserName(userElement);
420 var password = passwordBox.value;
421 if (password.length > 0) {
422 chrome.send('authenticateUser', [userName, password]);
423 return false;
424 }
425 }
426 return true;
427 },
428
429 loginKeyDown: function(e) {
430 switch (e.keyCode) {
431 // shift key down
432 case 16:
433 this.isShiftPressed_ = true;
434 break;
435 // ctrl key down
436 case 17:
437 this.isCtrlPressed_ = true;
438 break;
439 // alt key down
440 case 18:
441 this.isAltPressed_ = true;
442 break;
443 }
444 var keystroke = String.fromCharCode(e.keyCode);
445 switch (keystroke) {
446 case 'U':
447 if (this.isAltPressed_) {
448 this.slider.currentCardIndex = 0;
449 // focus the email box.
450 var emailBox =
451 $('add-user-card').getElementsByClassName('emailbox')[0];
452 assert(emailBox, 'Add User e-mail box not found');
453 emailBox.focus();
454 }
455 break;
456 case 'P':
457 if (this.isAltPressed_) {
458 if (this.slider.currentCard == null) {
459 this.slider.currentCardIndex = 0;
460 }
461 var passwordBox =
462 this.slider.currentCard.getElementsByClassName('passwordbox')[0];
463 assert(passwordBox, 'password box not found');
464 passwordBox.focus();
465 }
466 break;
467 case 'B':
468 if (this.isAltPressed_) {
469 var guestElement =
470 this.guestCard_.getElementsByClassName('user')[0];
471 var guestIndex = guestElement.getAttribute('user-index');
472 assert(guestIndex, 'unexpected user without userIndex');
473 this.slider.currentCardIndex = guestIndex;
474 setTimeout(function() {
475 // TODO(fsamuel): Make this call a common function.
476 chrome.send('launchIncognito', []);
477 }, 50);
478 }
479 break;
480 default:
481 switch (e.keyCode) {
482 // enter pressed.
483 case 13:
484 if (this.slider.currentCard == guestCard_) {
485 // TODO(fsamuel): Enter Incognito mode
486 }
487 break;
488 // escape pressed.
489 case 27:
490 // If we pressed the escape key then don't select any user.
491 this.slider.currentCardIndex = null;
492 break;
493 // left arrow pressed.
494 case 37:
495 if (this.slider.currentCardIndex == null) {
496 // TODO(fsamuel): This should be go to last active card + 1
497 this.slider.currentCardIndex = this.slider.cardCount() - 1;
498 } else if (this.slider.currentCardIndex > 0) {
499 this.slider.currentCardIndex -= 1;
500 }
501 break;
502 // right arrow pressed.
503 case 39:
504 if (this.slider.currentCardIndex == null) {
505 // TODO(fsamuel): This should be go to last active card - 1
506 this.slider.currentCardIndex = 0;
507 } else if (this.slider.currentCardIndex <
508 this.slider.cardCount() - 1) {
509 this.slider.currentCardIndex += 1;
510 }
511 break;
512 } // switch
513 } // switch
514 },
515
516 loginKeyUp: function(e) {
517 switch (e.keyCode) {
518 // shift key up.
519 case 16:
520 this.isShiftPressed_ = false;
521 break;
522 // ctrl key up.
523 case 17:
524 this.isCtrlPressed_ = false;
525 break;
526 // alt key up.
527 case 18:
528 this.isAltPressed_ = false;
529 break;
530 }
531 },
532
533 /**
534 * Creates the Add User card and inserts it into the slider.
535 * TODO(fsamuel): This is here only temporarily. We should eventually remove
536 * this card.
537 */
538 initializeAddUserCard: function(e) {
539 var emailBox = $('add-user-card').getElementsByClassName('emailbox')[0];
540 assert(emailBox, 'Add User e-mail box not found');
541 var passwordBox =
542 $('add-user-card').getElementsByClassName('passwordbox')[0];
543 assert(passwordBox, 'Add User password box not found');
544
545 var user = $('add-user-card').getElementsByClassName('user')[0];
546 // Clicking the Add User icon hides the icon and shows
547 // the user name and password input boxes
548 var self = this;
549 $('add-user').addEventListener('click', function(e) {
550 self.userClick(e);
551 });
552 $('add-user-card').addEventListener(Slider.EventType.ACTIVATE,
553 function(e) {
554 //$('add-user-spacer').style.display = 'block';
555 // Focus the card.
556 user.classList.add('focus');
557
558 // Hide the Add User Icon, and show the input boxes.
559 $('add-user').parentNode.hidden = true;
560 $('add-user-box').hidden = false;
561
562 // Focus the email box and clear the password box.
563 emailBox.focus();
564 passwordBox.value = '';
565 }, false);
566
567 $('add-user-card').addEventListener(Slider.EventType.DEACTIVATE,
568 function(e) {
569 //$('add-user-spacer').style.display = 'none';
570 // Unfocus the card.
571 user.classList.remove('focus');
572
573 $('add-user').parentNode.hidden = false;
574 $('add-user-box').hidden = true;
575 document.body.focus();
576 }, false);
577
578 // Prevent blurring of a textfield if focus is shifted to the body.
579 // This makes sure that the keyboard remains up until we hide the
580 // Add User box.
581 var focusedElement = null;
582 emailBox.addEventListener('focus', function(e) {
583 focusedElement = emailBox;
584 });
585 passwordBox.addEventListener('focus', function(e) {
586 focusedElement = passwordBox;
587 });
588 document.body.addEventListener('focus', function(e) {
589 if (focusedElement) {
590 focusedElement.focus();
591 e.preventDefault();
592 }
593 });
594
595 // Prevent the slider from receiving click events and canceling
596 // the Add User card.
597 $('add-user-box').addEventListener('click', function(e) {
598 e.preventDefault();
599 e.stopPropagation();
600 }, false);
601
602 emailBox.addEventListener('keypress', function(e) {
603 e.stopPropagation();
604 if (e.keyCode == 13) {
605 if (emailBox.value.length > 0) {
606 passwordBox.focus();
607 }
608 }
609 });
610
611 passwordBox.addEventListener('keypress', function(e) {
612 if (e.keyCode == 13) {
613 chrome.send('authenticateUser', [emailBox.value, passwordBox.value]);
614 }
615 }, true);
616 },
617
618 initialize: function() {
619 this.userEvents_ = new EventTracker();
620 // Request data on the users so we can fill them in.
621 // Note that this is kicked off asynchronously. 'getUsersCallback' will
622 // be invoked at some point after this function returns.
623 chrome.send('getUsers');
624
625 // Prevent touch events from triggering any sort of native scrolling
626 document.addEventListener('touchmove', function(e) {
627 e.preventDefault();
628 }, true);
629
630 this.userCardTemplate_ = $('user-template');
631 this.userCardTemplate_.id = null;
632 $('user-list').removeChild(this.userCardTemplate_);
633 $('user-frame').addEventListener(Slider.EventType.SELECTION_CHANGED,
634 this.userFrameSelectionChanged);
635
636 // Initialize the slider without any cards at the moment
637 this.slider = new Slider($('user-frame'), $('user-list'), []);
638 this.slider.initialize();
639
640 this.initializeAddUserCard();
641
642 // Bind on bubble phase
643 var self = this;
644 $('user-frame').addEventListener('click', function() {
645 self.slider.currentCardIndex = null;
646 }, false);
647 document.addEventListener('keydown', function(e) {
648 self.loginKeyDown(e);
649 }, true);
650 document.addEventListener('keyup', function(e) {
651 self.loginKeyUp(e);
652 }, true);
653 }
654 };
655
656 return LoginScreen;
657 })();
658
659
660 var getUsersCallback = null;
661
662 document.addEventListener('DOMContentLoaded', function() {
663 var loginScreenObj = new LoginScreen();
664 getUsersCallback = function(users) {
665 // call with the appropriate this.
666 loginScreenObj.getUsersCallback(users);
667 }
668 loginScreenObj.initialize();
669 }, false);
670
671 // Disable text selection.
672 document.onselectstart = function(e) {
673 e.preventDefault();
674 }
675
676 // Disable dragging.
677 document.ondragstart = function(e) {
678 e.preventDefault();
679 }
OLDNEW
« no previous file with comments | « chrome/browser/resources/login.html ('k') | chrome/browser/resources/login_ui.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698