1 // Copyright (c) 2012 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 Display manager for WebUI OOBE and login. 7 */ 8 9 // TODO(xiyuan): Find a better to share those constants. 10 /** @const */ var SCREEN_OOBE_NETWORK = 'connect'; 11 /** @const */ var SCREEN_OOBE_EULA = 'eula'; 12 /** @const */ var SCREEN_OOBE_UPDATE = 'update'; 13 /** @const */ var SCREEN_OOBE_ENROLLMENT = 'oauth-enrollment'; 14 /** @const */ var SCREEN_OOBE_KIOSK_ENABLE = 'kiosk-enable'; 15 /** @const */ var SCREEN_GAIA_SIGNIN = 'gaia-signin'; 16 /** @const */ var SCREEN_ACCOUNT_PICKER = 'account-picker'; 17 /** @const */ var SCREEN_ERROR_MESSAGE = 'error-message'; 18 /** @const */ var SCREEN_USER_IMAGE_PICKER = 'user-image'; 19 /** @const */ var SCREEN_TPM_ERROR = 'tpm-error-message'; 20 /** @const */ var SCREEN_PASSWORD_CHANGED = 'password-changed'; 21 /** @const */ var SCREEN_CREATE_MANAGED_USER_FLOW = 22 'managed-user-creation'; 23 /** @const */ var SCREEN_APP_LAUNCH_SPLASH = 'app-launch-splash'; 24 /** @const */ var SCREEN_CONFIRM_PASSWORD = 'confirm-password'; 25 /** @const */ var SCREEN_MESSAGE_BOX = 'message-box'; 26 27 /* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */ 28 /** @const */ var ACCELERATOR_CANCEL = 'cancel'; 29 /** @const */ var ACCELERATOR_ENROLLMENT = 'enrollment'; 30 /** @const */ var ACCELERATOR_KIOSK_ENABLE = 'kiosk_enable'; 31 /** @const */ var ACCELERATOR_VERSION = 'version'; 32 /** @const */ var ACCELERATOR_RESET = 'reset'; 33 /** @const */ var ACCELERATOR_FOCUS_PREV = 'focus_prev'; 34 /** @const */ var ACCELERATOR_FOCUS_NEXT = 'focus_next'; 35 /** @const */ var ACCELERATOR_DEVICE_REQUISITION = 'device_requisition'; 36 /** @const */ var ACCELERATOR_DEVICE_REQUISITION_REMORA = 37 'device_requisition_remora'; 38 /** @const */ var ACCELERATOR_APP_LAUNCH_BAILOUT = 'app_launch_bailout'; 39 40 /* Signin UI state constants. Used to control header bar UI. */ 41 /** @const */ var SIGNIN_UI_STATE = { 42 HIDDEN: 0, 43 GAIA_SIGNIN: 1, 44 ACCOUNT_PICKER: 2, 45 WRONG_HWID_WARNING: 3, 46 MANAGED_USER_CREATION_FLOW: 4, 47 }; 48 49 /* Possible UI states of the error screen. */ 50 /** @const */ var ERROR_SCREEN_UI_STATE = { 51 UNKNOWN: 'ui-state-unknown', 52 UPDATE: 'ui-state-update', 53 SIGNIN: 'ui-state-signin', 54 MANAGED_USER_CREATION_FLOW: 'ui-state-locally-managed', 55 KIOSK_MODE: 'ui-state-kiosk-mode', 56 LOCAL_STATE_ERROR: 'ui-state-local-state-error' 57 }; 58 59 /* Possible types of UI. */ 60 /** @const */ var DISPLAY_TYPE = { 61 UNKNOWN: 'unknown', 62 OOBE: 'oobe', 63 LOGIN: 'login', 64 LOCK: 'lock', 65 USER_ADDING: 'user-adding', 66 APP_LAUNCH_SPLASH: 'app-launch-splash', 67 DESKTOP_USER_MANAGER: 'login-add-user' 68 }; 69 70 cr.define('cr.ui.login', function() { 71 var Bubble = cr.ui.Bubble; 72 73 /** 74 * Maximum time in milliseconds to wait for step transition to finish. 75 * The value is used as the duration for ensureTransitionEndEvent below. 76 * It needs to be inline with the step screen transition duration time 77 * defined in css file. The current value in css is 200ms. To avoid emulated 78 * webkitTransitionEnd fired before real one, 250ms is used. 79 * @const 80 */ 81 var MAX_SCREEN_TRANSITION_DURATION = 250; 82 83 /** 84 * Groups of screens (screen IDs) that should have the same dimensions. 85 * @type Array.<Array.<string>> 86 * @const 87 */ 88 var SCREEN_GROUPS = [[SCREEN_OOBE_NETWORK, 89 SCREEN_OOBE_EULA, 90 SCREEN_OOBE_UPDATE] 91 ]; 92 93 /** 94 * Constructor a display manager that manages initialization of screens, 95 * transitions, error messages display. 96 * 97 * @constructor 98 */ 99 function DisplayManager() { 100 } 101 102 DisplayManager.prototype = { 103 /** 104 * Registered screens. 105 */ 106 screens_: [], 107 108 /** 109 * Current OOBE step, index in the screens array. 110 * @type {number} 111 */ 112 currentStep_: 0, 113 114 /** 115 * Whether version label can be toggled by ACCELERATOR_VERSION. 116 * @type {boolean} 117 */ 118 allowToggleVersion_: false, 119 120 /** 121 * Whether keyboard navigation flow is enforced. 122 * @type {boolean} 123 */ 124 forceKeyboardFlow_: false, 125 126 /** 127 * Type of UI. 128 * @type {string} 129 */ 130 displayType_: DISPLAY_TYPE.UNKNOWN, 131 132 get displayType() { 133 return this.displayType_; 134 }, 135 136 set displayType(displayType) { 137 this.displayType_ = displayType; 138 document.documentElement.setAttribute('screen', displayType); 139 }, 140 141 /** 142 * Returns dimensions of screen exluding header bar. 143 * @type {Object} 144 */ 145 get clientAreaSize() { 146 var container = $('outer-container'); 147 return {width: container.offsetWidth, height: container.offsetHeight}; 148 }, 149 150 /** 151 * Gets current screen element. 152 * @type {HTMLElement} 153 */ 154 get currentScreen() { 155 return $(this.screens_[this.currentStep_]); 156 }, 157 158 /** 159 * Hides/shows header (Shutdown/Add User/Cancel buttons). 160 * @param {boolean} hidden Whether header is hidden. 161 */ 162 set headerHidden(hidden) { 163 $('login-header-bar').hidden = hidden; 164 }, 165 166 /** 167 * Toggles background of main body between transparency and solid. 168 * @param {boolean} solid Whether to show a solid background. 169 */ 170 set solidBackground(solid) { 171 if (solid) 172 document.body.classList.add('solid'); 173 else 174 document.body.classList.remove('solid'); 175 }, 176 177 /** 178 * Forces keyboard based OOBE navigation. 179 * @param {boolean} value True if keyboard navigation flow is forced. 180 */ 181 set forceKeyboardFlow(value) { 182 this.forceKeyboardFlow_ = value; 183 if (value) { 184 keyboard.initializeKeyboardFlow(); 185 cr.ui.Dropdown.enableKeyboardFlow(); 186 } 187 }, 188 189 /** 190 * Shows/hides version labels. 191 * @param {boolean} show Whether labels should be visible by default. If 192 * false, visibility can be toggled by ACCELERATOR_VERSION. 193 */ 194 showVersion: function(show) { 195 $('version-labels').hidden = !show; 196 this.allowToggleVersion_ = !show; 197 }, 198 199 /** 200 * Handle accelerators. 201 * @param {string} name Accelerator name. 202 */ 203 handleAccelerator: function(name) { 204 if (name == ACCELERATOR_CANCEL) { 205 if (this.currentScreen.cancel) { 206 this.currentScreen.cancel(); 207 } 208 } else if (name == ACCELERATOR_ENROLLMENT) { 209 var currentStepId = this.screens_[this.currentStep_]; 210 if (currentStepId == SCREEN_GAIA_SIGNIN || 211 currentStepId == SCREEN_ACCOUNT_PICKER) { 212 chrome.send('toggleEnrollmentScreen'); 213 } else if (currentStepId == SCREEN_OOBE_NETWORK || 214 currentStepId == SCREEN_OOBE_EULA) { 215 // In this case update check will be skipped and OOBE will 216 // proceed straight to enrollment screen when EULA is accepted. 217 chrome.send('skipUpdateEnrollAfterEula'); 218 } else if (currentStepId == SCREEN_OOBE_ENROLLMENT) { 219 // This accelerator is also used to manually cancel auto-enrollment. 220 if (this.currentScreen.cancelAutoEnrollment) 221 this.currentScreen.cancelAutoEnrollment(); 222 } 223 } else if (name == ACCELERATOR_KIOSK_ENABLE) { 224 var currentStepId = this.screens_[this.currentStep_]; 225 if (currentStepId == SCREEN_GAIA_SIGNIN || 226 currentStepId == SCREEN_ACCOUNT_PICKER) { 227 chrome.send('toggleKioskEnableScreen'); 228 } 229 } else if (name == ACCELERATOR_VERSION) { 230 if (this.allowToggleVersion_) 231 $('version-labels').hidden = !$('version-labels').hidden; 232 } else if (name == ACCELERATOR_RESET) { 233 var currentStepId = this.screens_[this.currentStep_]; 234 if (currentStepId == SCREEN_GAIA_SIGNIN || 235 currentStepId == SCREEN_ACCOUNT_PICKER) { 236 chrome.send('toggleResetScreen'); 237 } 238 } else if (name == ACCELERATOR_DEVICE_REQUISITION) { 239 if (this.isOobeUI()) 240 this.showDeviceRequisitionPrompt_(); 241 } else if (name == ACCELERATOR_DEVICE_REQUISITION_REMORA) { 242 if (this.isOobeUI()) 243 this.showDeviceRequisitionRemoraPrompt_(); 244 } else if (name == ACCELERATOR_APP_LAUNCH_BAILOUT) { 245 var currentStepId = this.screens_[this.currentStep_]; 246 if (currentStepId == SCREEN_APP_LAUNCH_SPLASH) 247 chrome.send('cancelAppLaunch'); 248 } 249 250 if (!this.forceKeyboardFlow_) 251 return; 252 253 // Handle special accelerators for keyboard enhanced navigation flow. 254 if (name == ACCELERATOR_FOCUS_PREV) 255 keyboard.raiseKeyFocusPrevious(document.activeElement); 256 else if (name == ACCELERATOR_FOCUS_NEXT) 257 keyboard.raiseKeyFocusNext(document.activeElement); 258 }, 259 260 /** 261 * Appends buttons to the button strip. 262 * @param {Array.<HTMLElement>} buttons Array with the buttons to append. 263 * @param {string} screenId Id of the screen that buttons belong to. 264 */ 265 appendButtons_: function(buttons, screenId) { 266 if (buttons) { 267 var buttonStrip = $(screenId + '-controls'); 268 if (buttonStrip) { 269 for (var i = 0; i < buttons.length; ++i) 270 buttonStrip.appendChild(buttons[i]); 271 } 272 } 273 }, 274 275 /** 276 * Disables or enables control buttons on the specified screen. 277 * @param {HTMLElement} screen Screen which controls should be affected. 278 * @param {boolean} disabled Whether to disable controls. 279 */ 280 disableButtons_: function(screen, disabled) { 281 var buttons = document.querySelectorAll( 282 '#' + screen.id + '-controls button:not(.preserve-disabled-state)'); 283 for (var i = 0; i < buttons.length; ++i) { 284 buttons[i].disabled = disabled; 285 } 286 }, 287 288 /** 289 * Updates a step's css classes to reflect left, current, or right position. 290 * @param {number} stepIndex step index. 291 * @param {string} state one of 'left', 'current', 'right'. 292 */ 293 updateStep_: function(stepIndex, state) { 294 var stepId = this.screens_[stepIndex]; 295 var step = $(stepId); 296 var header = $('header-' + stepId); 297 var states = ['left', 'right', 'current']; 298 for (var i = 0; i < states.length; ++i) { 299 if (states[i] != state) { 300 step.classList.remove(states[i]); 301 header.classList.remove(states[i]); 302 } 303 } 304 step.classList.add(state); 305 header.classList.add(state); 306 }, 307 308 /** 309 * Switches to the next OOBE step. 310 * @param {number} nextStepIndex Index of the next step. 311 */ 312 toggleStep_: function(nextStepIndex, screenData) { 313 var currentStepId = this.screens_[this.currentStep_]; 314 var nextStepId = this.screens_[nextStepIndex]; 315 var oldStep = $(currentStepId); 316 var newStep = $(nextStepId); 317 var newHeader = $('header-' + nextStepId); 318 319 // Disable controls before starting animation. 320 this.disableButtons_(oldStep, true); 321 322 if (oldStep.onBeforeHide) 323 oldStep.onBeforeHide(); 324 325 $('oobe').className = nextStepId; 326 327 if (newStep.onBeforeShow) 328 newStep.onBeforeShow(screenData); 329 330 newStep.classList.remove('hidden'); 331 332 if (this.isOobeUI()) { 333 // Start gliding animation for OOBE steps. 334 if (nextStepIndex > this.currentStep_) { 335 for (var i = this.currentStep_; i < nextStepIndex; ++i) 336 this.updateStep_(i, 'left'); 337 this.updateStep_(nextStepIndex, 'current'); 338 } else if (nextStepIndex < this.currentStep_) { 339 for (var i = this.currentStep_; i > nextStepIndex; --i) 340 this.updateStep_(i, 'right'); 341 this.updateStep_(nextStepIndex, 'current'); 342 } 343 } else { 344 // Start fading animation for login display. 345 oldStep.classList.add('faded'); 346 newStep.classList.remove('faded'); 347 } 348 349 this.disableButtons_(newStep, false); 350 351 // Adjust inner container height based on new step's height. 352 this.updateScreenSize(newStep); 353 354 if (newStep.onAfterShow) 355 newStep.onAfterShow(screenData); 356 357 // Default control to be focused (if specified). 358 var defaultControl = newStep.defaultControl; 359 360 var innerContainer = $('inner-container'); 361 if (this.currentStep_ != nextStepIndex && 362 !oldStep.classList.contains('hidden')) { 363 if (oldStep.classList.contains('animated')) { 364 innerContainer.classList.add('animation'); 365 oldStep.addEventListener('webkitTransitionEnd', function f(e) { 366 oldStep.removeEventListener('webkitTransitionEnd', f); 367 if (oldStep.classList.contains('faded') || 368 oldStep.classList.contains('left') || 369 oldStep.classList.contains('right')) { 370 innerContainer.classList.remove('animation'); 371 oldStep.classList.add('hidden'); 372 } 373 // Refresh defaultControl. It could have changed. 374 var defaultControl = newStep.defaultControl; 375 if (defaultControl) 376 defaultControl.focus(); 377 }); 378 ensureTransitionEndEvent(oldStep, MAX_SCREEN_TRANSITION_DURATION); 379 } else { 380 oldStep.classList.add('hidden'); 381 if (defaultControl) 382 defaultControl.focus(); 383 } 384 } else { 385 // First screen on OOBE launch. 386 if (this.isOobeUI() && innerContainer.classList.contains('down')) { 387 innerContainer.classList.remove('down'); 388 innerContainer.addEventListener( 389 'webkitTransitionEnd', function f(e) { 390 innerContainer.removeEventListener('webkitTransitionEnd', f); 391 $('progress-dots').classList.remove('down'); 392 chrome.send('loginVisible', ['oobe']); 393 // Refresh defaultControl. It could have changed. 394 var defaultControl = newStep.defaultControl; 395 if (defaultControl) 396 defaultControl.focus(); 397 }); 398 ensureTransitionEndEvent(innerContainer, 399 MAX_SCREEN_TRANSITION_DURATION); 400 } else { 401 if (defaultControl) 402 defaultControl.focus(); 403 chrome.send('loginVisible', ['oobe']); 404 } 405 } 406 this.currentStep_ = nextStepIndex; 407 408 $('step-logo').hidden = newStep.classList.contains('no-logo'); 409 410 chrome.send('updateCurrentScreen', [this.currentScreen.id]); 411 }, 412 413 /** 414 * Make sure that screen is initialized and decorated. 415 * @param {Object} screen Screen params dict, e.g. {id: screenId, data: {}}. 416 */ 417 preloadScreen: function(screen) { 418 var screenEl = $(screen.id); 419 if (screenEl.deferredDecorate !== undefined) { 420 screenEl.deferredDecorate(); 421 delete screenEl.deferredDecorate; 422 } 423 }, 424 425 /** 426 * Show screen of given screen id. 427 * @param {Object} screen Screen params dict, e.g. {id: screenId, data: {}}. 428 */ 429 showScreen: function(screen) { 430 var screenId = screen.id; 431 432 // Make sure the screen is decorated. 433 this.preloadScreen(screen); 434 435 if (screen.data !== undefined && screen.data.disableAddUser) 436 DisplayManager.updateAddUserButtonStatus(true); 437 438 439 // Show sign-in screen instead of account picker if pod row is empty. 440 if (screenId == SCREEN_ACCOUNT_PICKER && $('pod-row').pods.length == 0) { 441 // Manually hide 'add-user' header bar, because of the case when 442 // 'Cancel' button is used on the offline login page. 443 $('add-user-header-bar-item').hidden = true; 444 Oobe.showSigninUI(true); 445 return; 446 } 447 448 var data = screen.data; 449 var index = this.getScreenIndex_(screenId); 450 if (index >= 0) 451 this.toggleStep_(index, data); 452 }, 453 454 /** 455 * Gets index of given screen id in screens_. 456 * @param {string} screenId Id of the screen to look up. 457 * @private 458 */ 459 getScreenIndex_: function(screenId) { 460 for (var i = 0; i < this.screens_.length; ++i) { 461 if (this.screens_[i] == screenId) 462 return i; 463 } 464 return -1; 465 }, 466 467 /** 468 * Register an oobe screen. 469 * @param {Element} el Decorated screen element. 470 */ 471 registerScreen: function(el) { 472 var screenId = el.id; 473 this.screens_.push(screenId); 474 475 var header = document.createElement('span'); 476 header.id = 'header-' + screenId; 477 header.textContent = el.header ? el.header : ''; 478 header.className = 'header-section'; 479 $('header-sections').appendChild(header); 480 481 var dot = document.createElement('div'); 482 dot.id = screenId + '-dot'; 483 dot.className = 'progdot'; 484 var progressDots = $('progress-dots'); 485 if (progressDots) 486 progressDots.appendChild(dot); 487 488 this.appendButtons_(el.buttons, screenId); 489 }, 490 491 /** 492 * Updates inner container size based on the size of the current screen and 493 * other screens in the same group. 494 * Should be executed on screen change / screen size change. 495 * @param {!HTMLElement} screen Screen that is being shown. 496 */ 497 updateScreenSize: function(screen) { 498 // Have to reset any previously predefined screen size first 499 // so that screen contents would define it instead. 500 $('inner-container').style.height = ''; 501 $('inner-container').style.width = ''; 502 screen.style.width = ''; 503 screen.style.height = ''; 504 505 $('outer-container').classList.toggle( 506 'fullscreen', screen.classList.contains('fullscreen')); 507 508 var width = screen.getPreferredSize().width; 509 var height = screen.getPreferredSize().height; 510 for (var i = 0, screenGroup; screenGroup = SCREEN_GROUPS[i]; i++) { 511 if (screenGroup.indexOf(screen.id) != -1) { 512 // Set screen dimensions to maximum dimensions within this group. 513 for (var j = 0, screen2; screen2 = $(screenGroup[j]); j++) { 514 width = Math.max(width, screen2.getPreferredSize().width); 515 height = Math.max(height, screen2.getPreferredSize().height); 516 } 517 break; 518 } 519 } 520 $('inner-container').style.height = height + 'px'; 521 $('inner-container').style.width = width + 'px'; 522 // This requires |screen| to have 'box-sizing: border-box'. 523 screen.style.width = width + 'px'; 524 screen.style.height = height + 'px'; 525 }, 526 527 /** 528 * Updates localized content of the screens like headers, buttons and links. 529 * Should be executed on language change. 530 */ 531 updateLocalizedContent_: function() { 532 for (var i = 0, screenId; screenId = this.screens_[i]; ++i) { 533 var screen = $(screenId); 534 var buttonStrip = $(screenId + '-controls'); 535 if (buttonStrip) 536 buttonStrip.innerHTML = ''; 537 // TODO(nkostylev): Update screen headers for new OOBE design. 538 this.appendButtons_(screen.buttons, screenId); 539 if (screen.updateLocalizedContent) 540 screen.updateLocalizedContent(); 541 } 542 543 var currentScreenId = this.screens_[this.currentStep_]; 544 var currentScreen = $(currentScreenId); 545 this.updateScreenSize(currentScreen); 546 547 // Trigger network drop-down to reload its state 548 // so that strings are reloaded. 549 // Will be reloaded if drowdown is actually shown. 550 cr.ui.DropDown.refresh(); 551 }, 552 553 /** 554 * Prepares screens to use in login display. 555 */ 556 prepareForLoginDisplay_: function() { 557 for (var i = 0, screenId; screenId = this.screens_[i]; ++i) { 558 var screen = $(screenId); 559 screen.classList.add('faded'); 560 screen.classList.remove('right'); 561 screen.classList.remove('left'); 562 } 563 }, 564 565 /** 566 * Shows the device requisition prompt. 567 */ 568 showDeviceRequisitionPrompt_: function() { 569 if (!this.deviceRequisitionDialog_) { 570 this.deviceRequisitionDialog_ = 571 new cr.ui.dialogs.PromptDialog(document.body); 572 this.deviceRequisitionDialog_.setOkLabel( 573 loadTimeData.getString('deviceRequisitionPromptOk')); 574 this.deviceRequisitionDialog_.setCancelLabel( 575 loadTimeData.getString('deviceRequisitionPromptCancel')); 576 } 577 this.deviceRequisitionDialog_.show( 578 loadTimeData.getString('deviceRequisitionPromptText'), 579 this.deviceRequisition_, 580 this.onConfirmDeviceRequisitionPrompt_.bind(this)); 581 }, 582 583 /** 584 * Confirmation handle for the device requisition prompt. 585 * @param {string} value The value entered by the user. 586 * @private 587 */ 588 onConfirmDeviceRequisitionPrompt_: function(value) { 589 this.deviceRequisition_ = value; 590 chrome.send('setDeviceRequisition', [value == '' ? 'none' : value]); 591 }, 592 593 /** 594 * Called when window size changed. Notifies current screen about change. 595 * @private 596 */ 597 onWindowResize_: function() { 598 var currentScreenId = this.screens_[this.currentStep_]; 599 var currentScreen = $(currentScreenId); 600 if (currentScreen) 601 currentScreen.onWindowResize(); 602 }, 603 604 /* 605 * Updates the device requisition string shown in the requisition prompt. 606 * @param {string} requisition The device requisition. 607 */ 608 updateDeviceRequisition: function(requisition) { 609 this.deviceRequisition_ = requisition; 610 }, 611 612 /** 613 * Shows the special remora device requisition prompt. 614 * @private 615 */ 616 showDeviceRequisitionRemoraPrompt_: function() { 617 if (!this.deviceRequisitionRemoraDialog_) { 618 this.deviceRequisitionRemoraDialog_ = 619 new cr.ui.dialogs.ConfirmDialog(document.body); 620 this.deviceRequisitionRemoraDialog_.setOkLabel( 621 loadTimeData.getString('deviceRequisitionRemoraPromptOk')); 622 this.deviceRequisitionRemoraDialog_.setCancelLabel( 623 loadTimeData.getString('deviceRequisitionRemoraPromptCancel')); 624 } 625 this.deviceRequisitionRemoraDialog_.showWithTitle( 626 loadTimeData.getString('deviceRequisitionRemoraPromptTitle'), 627 loadTimeData.getString('deviceRequisitionRemoraPromptText'), 628 function() { // onShow 629 chrome.send('setDeviceRequisition', ['remora']); 630 }, 631 function() { // onCancel 632 chrome.send('setDeviceRequisition', ['none']); 633 }); 634 }, 635 636 /** 637 * Returns true if Oobe UI is shown. 638 */ 639 isOobeUI: function() { 640 return document.body.classList.contains('oobe-display'); 641 }, 642 643 /** 644 * Returns true if sign in UI should trigger wallpaper load on boot. 645 */ 646 shouldLoadWallpaperOnBoot: function() { 647 return loadTimeData.getString('bootIntoWallpaper') == 'on'; 648 }, 649 650 /** 651 * Sets or unsets given |className| for top-level container. Useful for 652 * customizing #inner-container with CSS rules. All classes set with with 653 * this method will be removed after screen change. 654 * @param {string} className Class to toggle. 655 * @param {boolean} enabled Whether class should be enabled or disabled. 656 */ 657 toggleClass: function(className, enabled) { 658 $('oobe').classList.toggle(className, enabled); 659 } 660 }; 661 662 /** 663 * Initializes display manager. 664 */ 665 DisplayManager.initialize = function() { 666 var givenDisplayType = DISPLAY_TYPE.UNKNOWN; 667 if (document.documentElement.hasAttribute('screen')) { 668 // Display type set in HTML property. 669 givenDisplayType = document.documentElement.getAttribute('screen'); 670 } else { 671 // Extracting display type from URL. 672 givenDisplayType = window.location.pathname.substr(1); 673 } 674 var instance = Oobe.getInstance(); 675 Object.getOwnPropertyNames(DISPLAY_TYPE).forEach(function(type) { 676 if (DISPLAY_TYPE[type] == givenDisplayType) { 677 instance.displayType = givenDisplayType; 678 } 679 }); 680 if (instance.displayType == DISPLAY_TYPE.UNKNOWN) { 681 console.error("Unknown display type '" + givenDisplayType + 682 "'. Setting default."); 683 instance.displayType = DISPLAY_TYPE.LOGIN; 684 } 685 686 window.addEventListener('resize', instance.onWindowResize_.bind(instance)); 687 }; 688 689 /** 690 * Returns offset (top, left) of the element. 691 * @param {!Element} element HTML element. 692 * @return {!Object} The offset (top, left). 693 */ 694 DisplayManager.getOffset = function(element) { 695 var x = 0; 696 var y = 0; 697 while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { 698 x += element.offsetLeft - element.scrollLeft; 699 y += element.offsetTop - element.scrollTop; 700 element = element.offsetParent; 701 } 702 return { top: y, left: x }; 703 }; 704 705 /** 706 * Returns position (top, left, right, bottom) of the element. 707 * @param {!Element} element HTML element. 708 * @return {!Object} Element position (top, left, right, bottom). 709 */ 710 DisplayManager.getPosition = function(element) { 711 var offset = DisplayManager.getOffset(element); 712 return { top: offset.top, 713 right: window.innerWidth - element.offsetWidth - offset.left, 714 bottom: window.innerHeight - element.offsetHeight - offset.top, 715 left: offset.left }; 716 }; 717 718 /** 719 * Disables signin UI. 720 */ 721 DisplayManager.disableSigninUI = function() { 722 $('login-header-bar').disabled = true; 723 $('pod-row').disabled = true; 724 }; 725 726 /** 727 * Shows signin UI. 728 * @param {string} opt_email An optional email for signin UI. 729 */ 730 DisplayManager.showSigninUI = function(opt_email) { 731 var currentScreenId = Oobe.getInstance().currentScreen.id; 732 if (currentScreenId == SCREEN_GAIA_SIGNIN) 733 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN; 734 else if (currentScreenId == SCREEN_ACCOUNT_PICKER) 735 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.ACCOUNT_PICKER; 736 chrome.send('showAddUser', [opt_email]); 737 }; 738 739 /** 740 * Resets sign-in input fields. 741 * @param {boolean} forceOnline Whether online sign-in should be forced. 742 * If |forceOnline| is false previously used sign-in type will be used. 743 */ 744 DisplayManager.resetSigninUI = function(forceOnline) { 745 var currentScreenId = Oobe.getInstance().currentScreen.id; 746 747 $(SCREEN_GAIA_SIGNIN).reset( 748 currentScreenId == SCREEN_GAIA_SIGNIN, forceOnline); 749 $('login-header-bar').disabled = false; 750 $('pod-row').reset(currentScreenId == SCREEN_ACCOUNT_PICKER); 751 }; 752 753 /** 754 * Shows sign-in error bubble. 755 * @param {number} loginAttempts Number of login attemps tried. 756 * @param {string} message Error message to show. 757 * @param {string} link Text to use for help link. 758 * @param {number} helpId Help topic Id associated with help link. 759 */ 760 DisplayManager.showSignInError = function(loginAttempts, message, link, 761 helpId) { 762 var error = document.createElement('div'); 763 764 var messageDiv = document.createElement('div'); 765 messageDiv.className = 'error-message-bubble'; 766 messageDiv.textContent = message; 767 error.appendChild(messageDiv); 768 769 if (link) { 770 messageDiv.classList.add('error-message-bubble-padding'); 771 772 var helpLink = document.createElement('a'); 773 helpLink.href = '#'; 774 helpLink.textContent = link; 775 helpLink.addEventListener('click', function(e) { 776 chrome.send('launchHelpApp', [helpId]); 777 e.preventDefault(); 778 }); 779 error.appendChild(helpLink); 780 } 781 782 var currentScreen = Oobe.getInstance().currentScreen; 783 if (currentScreen && typeof currentScreen.showErrorBubble === 'function') 784 currentScreen.showErrorBubble(loginAttempts, error); 785 }; 786 787 /** 788 * Shows password changed screen that offers migration. 789 * @param {boolean} showError Whether to show the incorrect password error. 790 */ 791 DisplayManager.showPasswordChangedScreen = function(showError) { 792 login.PasswordChangedScreen.show(showError); 793 }; 794 795 /** 796 * Shows dialog to create managed user. 797 */ 798 DisplayManager.showManagedUserCreationScreen = function() { 799 login.ManagedUserCreationScreen.show(); 800 }; 801 802 /** 803 * Shows TPM error screen. 804 */ 805 DisplayManager.showTpmError = function() { 806 login.TPMErrorMessageScreen.show(); 807 }; 808 809 /** 810 * Clears error bubble. 811 */ 812 DisplayManager.clearErrors = function() { 813 $('bubble').hide(); 814 }; 815 816 /** 817 * Sets text content for a div with |labelId|. 818 * @param {string} labelId Id of the label div. 819 * @param {string} labelText Text for the label. 820 */ 821 DisplayManager.setLabelText = function(labelId, labelText) { 822 $(labelId).textContent = labelText; 823 }; 824 825 /** 826 * Sets the text content of the enterprise info message. 827 * @param {string} messageText The message text. 828 */ 829 DisplayManager.setEnterpriseInfo = function(messageText) { 830 $('enterprise-info-message').textContent = messageText; 831 if (messageText) { 832 $('enterprise-info').hidden = false; 833 } 834 }; 835 836 /** 837 * Disable Add users button if said. 838 * @param {boolean} disable true to disable 839 */ 840 DisplayManager.updateAddUserButtonStatus = function(disable) { 841 $('add-user-button').disabled = disable; 842 $('add-user-button').classList[ 843 disable ? 'add' : 'remove']('button-restricted'); 844 $('add-user-button').title = disable ? 845 loadTimeData.getString('disabledAddUserTooltip') : ''; 846 } 847 848 /** 849 * Clears password field in user-pod. 850 */ 851 DisplayManager.clearUserPodPassword = function() { 852 $('pod-row').clearFocusedPod(); 853 }; 854 855 /** 856 * Restores input focus to currently selected pod. 857 */ 858 DisplayManager.refocusCurrentPod = function() { 859 $('pod-row').refocusCurrentPod(); 860 }; 861 862 // Export 863 return { 864 DisplayManager: DisplayManager 865 }; 866 }); 867