OLD | NEW |
(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 Display manager for WebUI OOBE and login. |
| 7 */ |
| 8 |
| 9 // TODO(xiyuan): Find a better to share those constants. |
| 10 const SCREEN_SIGNIN = 'signin'; |
| 11 const SCREEN_GAIA_SIGNIN = 'gaia-signin'; |
| 12 const SCREEN_ACCOUNT_PICKER = 'account-picker'; |
| 13 |
| 14 /* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */ |
| 15 const ACCELERATOR_ACCESSIBILITY = 'accessibility'; |
| 16 const ACCELERATOR_CANCEL = 'cancel'; |
| 17 const ACCELERATOR_ENROLLMENT = 'enrollment'; |
| 18 |
| 19 cr.define('cr.ui.login', function() { |
| 20 /** |
| 21 * Constructor a display manager that manages initialization of screens, |
| 22 * transitions, error messages display. |
| 23 * |
| 24 * @constructor |
| 25 */ |
| 26 function DisplayManager() { |
| 27 } |
| 28 |
| 29 DisplayManager.prototype = { |
| 30 /** |
| 31 * Registered screens. |
| 32 */ |
| 33 screens_: [], |
| 34 |
| 35 /** |
| 36 * Current OOBE step, index in the screens array. |
| 37 * @type {number} |
| 38 */ |
| 39 currentStep_: 0, |
| 40 |
| 41 /** |
| 42 * Gets current screen element. |
| 43 * @type {HTMLElement} |
| 44 */ |
| 45 get currentScreen() { |
| 46 return $(this.screens_[this.currentStep_]); |
| 47 }, |
| 48 |
| 49 /** |
| 50 * Hides/shows header (Shutdown/Add User/Cancel buttons). |
| 51 * @param {bool} hidden Whether header is hidden. |
| 52 */ |
| 53 set headerHidden(hidden) { |
| 54 $('login-header-bar').hidden = hidden; |
| 55 }, |
| 56 |
| 57 /** |
| 58 * Handle accelerators. |
| 59 * @param {string} name Accelerator name. |
| 60 */ |
| 61 handleAccelerator: function(name) { |
| 62 if (name == ACCELERATOR_ACCESSIBILITY) { |
| 63 chrome.send('toggleAccessibility', []); |
| 64 } else if (name == ACCELERATOR_CANCEL) { |
| 65 if (this.currentScreen.cancel) { |
| 66 this.currentScreen.cancel(); |
| 67 } |
| 68 } else if (ACCELERATOR_ENROLLMENT) { |
| 69 var currentStepId = this.screens_[this.currentStep_]; |
| 70 if (currentStepId == SCREEN_SIGNIN || |
| 71 currentStepId == SCREEN_GAIA_SIGNIN) { |
| 72 chrome.send('toggleEnrollmentScreen', []); |
| 73 } |
| 74 } |
| 75 }, |
| 76 |
| 77 /** |
| 78 * Appends buttons to the button strip. |
| 79 * @param {Array} buttons Array with the buttons to append. |
| 80 */ |
| 81 appendButtons_ : function(buttons) { |
| 82 if (buttons) { |
| 83 var buttonStrip = $('button-strip'); |
| 84 for (var i = 0; i < buttons.length; ++i) { |
| 85 var button = buttons[i]; |
| 86 buttonStrip.appendChild(button); |
| 87 } |
| 88 } |
| 89 }, |
| 90 |
| 91 /** |
| 92 * Updates a step's css classes to reflect left, current, or right position. |
| 93 * @param {number} stepIndex step index. |
| 94 * @param {string} state one of 'left', 'current', 'right'. |
| 95 */ |
| 96 updateStep_: function(stepIndex, state) { |
| 97 var stepId = this.screens_[stepIndex]; |
| 98 var step = $(stepId); |
| 99 var header = $('header-' + stepId); |
| 100 var states = [ 'left', 'right', 'current' ]; |
| 101 for (var i = 0; i < states.length; ++i) { |
| 102 if (states[i] != state) { |
| 103 step.classList.remove(states[i]); |
| 104 header.classList.remove(states[i]); |
| 105 } |
| 106 } |
| 107 step.classList.add(state); |
| 108 header.classList.add(state); |
| 109 }, |
| 110 |
| 111 /** |
| 112 * Switches to the next OOBE step. |
| 113 * @param {number} nextStepIndex Index of the next step. |
| 114 */ |
| 115 toggleStep_: function(nextStepIndex, screenData) { |
| 116 var currentStepId = this.screens_[this.currentStep_]; |
| 117 var nextStepId = this.screens_[nextStepIndex]; |
| 118 var oldStep = $(currentStepId); |
| 119 var newStep = $(nextStepId); |
| 120 var newHeader = $('header-' + nextStepId); |
| 121 |
| 122 if (oldStep.onBeforeHide) |
| 123 oldStep.onBeforeHide(); |
| 124 |
| 125 if (newStep.onBeforeShow) |
| 126 newStep.onBeforeShow(screenData); |
| 127 |
| 128 newStep.classList.remove('hidden'); |
| 129 |
| 130 if (this.isOobeUI()) { |
| 131 // Start gliding animation for OOBE steps. |
| 132 if (nextStepIndex > this.currentStep_) { |
| 133 for (var i = this.currentStep_; i < nextStepIndex; ++i) |
| 134 this.updateStep_(i, 'left'); |
| 135 this.updateStep_(nextStepIndex, 'current'); |
| 136 } else if (nextStepIndex < this.currentStep_) { |
| 137 for (var i = this.currentStep_; i > nextStepIndex; --i) |
| 138 this.updateStep_(i, 'right'); |
| 139 this.updateStep_(nextStepIndex, 'current'); |
| 140 } |
| 141 } else { |
| 142 // Start fading animation for login display. |
| 143 oldStep.classList.add('faded'); |
| 144 newStep.classList.remove('faded'); |
| 145 } |
| 146 |
| 147 // Adjust inner container height based on new step's height. |
| 148 $('inner-container').style.height = newStep.offsetHeight + 'px'; |
| 149 |
| 150 if (this.currentStep_ != nextStepIndex && |
| 151 !oldStep.classList.contains('hidden')) { |
| 152 oldStep.addEventListener('webkitTransitionEnd', function f(e) { |
| 153 oldStep.removeEventListener('webkitTransitionEnd', f); |
| 154 oldStep.classList.add('hidden'); |
| 155 }); |
| 156 } else { |
| 157 // First screen on OOBE launch. |
| 158 newHeader.classList.remove('right'); |
| 159 } |
| 160 this.currentStep_ = nextStepIndex; |
| 161 $('oobe').className = nextStepId; |
| 162 }, |
| 163 |
| 164 /** |
| 165 * Show screen of given screen id. |
| 166 * @param {Object} screen Screen params dict, |
| 167 * e.g. {id: screenId, data: data} |
| 168 */ |
| 169 showScreen: function(screen) { |
| 170 var screenId = screen.id; |
| 171 |
| 172 // Show sign-in screen instead of account picker if pod row is empty. |
| 173 if (screenId == SCREEN_ACCOUNT_PICKER && $('pod-row').pods.length == 0) { |
| 174 Oobe.showSigninUI(); |
| 175 return; |
| 176 } |
| 177 |
| 178 var data = screen.data; |
| 179 var index = this.getScreenIndex_(screenId); |
| 180 if (index >= 0) |
| 181 this.toggleStep_(index, data); |
| 182 $('offline-message').update(); |
| 183 }, |
| 184 |
| 185 /** |
| 186 * Gets index of given screen id in screens_. |
| 187 * @param {string} screenId Id of the screen to look up. |
| 188 * @private |
| 189 */ |
| 190 getScreenIndex_: function(screenId) { |
| 191 for (var i = 0; i < this.screens_.length; ++i) { |
| 192 if (this.screens_[i] == screenId) |
| 193 return i; |
| 194 } |
| 195 return -1; |
| 196 }, |
| 197 |
| 198 /** |
| 199 * Register an oobe screen. |
| 200 * @param {Element} el Decorated screen element. |
| 201 */ |
| 202 registerScreen: function(el) { |
| 203 var screenId = el.id; |
| 204 this.screens_.push(screenId); |
| 205 |
| 206 var header = document.createElement('span'); |
| 207 header.id = 'header-' + screenId; |
| 208 header.className = 'header-section right'; |
| 209 header.textContent = el.header ? el.header : ''; |
| 210 $('header-sections').appendChild(header); |
| 211 |
| 212 var dot = document.createElement('div'); |
| 213 dot.id = screenId + '-dot'; |
| 214 dot.className = 'progdot'; |
| 215 $('progress').appendChild(dot); |
| 216 |
| 217 this.appendButtons_(el.buttons); |
| 218 }, |
| 219 |
| 220 /** |
| 221 * Updates headers and buttons of the screens. |
| 222 * Should be executed on language change. |
| 223 */ |
| 224 updateHeadersAndButtons_: function() { |
| 225 $('button-strip').innerHTML = ''; |
| 226 for (var i = 0, screenId; screenId = this.screens_[i]; ++i) { |
| 227 var screen = $(screenId); |
| 228 $('header-' + screenId).textContent = screen.header; |
| 229 this.appendButtons_(screen.buttons); |
| 230 } |
| 231 }, |
| 232 |
| 233 /** |
| 234 * Prepares screens to use in login display. |
| 235 */ |
| 236 prepareForLoginDisplay_ : function() { |
| 237 for (var i = 0, screenId; screenId = this.screens_[i]; ++i) { |
| 238 var screen = $(screenId); |
| 239 screen.classList.add('faded'); |
| 240 screen.classList.remove('right'); |
| 241 screen.classList.remove('left'); |
| 242 } |
| 243 }, |
| 244 |
| 245 /** |
| 246 * Returns true if Oobe UI is shown. |
| 247 */ |
| 248 isOobeUI: function() { |
| 249 return !document.body.classList.contains('login-display'); |
| 250 } |
| 251 }; |
| 252 |
| 253 /** |
| 254 * Returns offset (top, left) of the element. |
| 255 * @param {!Element} element HTML element |
| 256 * @return {!Object} The offset (top, left). |
| 257 */ |
| 258 DisplayManager.getOffset = function(element) { |
| 259 var x = 0; |
| 260 var y = 0; |
| 261 while(element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { |
| 262 x += element.offsetLeft - element.scrollLeft; |
| 263 y += element.offsetTop - element.scrollTop; |
| 264 element = element.offsetParent; |
| 265 } |
| 266 return { top: y, left: x }; |
| 267 }; |
| 268 |
| 269 /** |
| 270 * Shows signin UI. |
| 271 * @param {string} opt_email An optional email for signin UI. |
| 272 */ |
| 273 DisplayManager.showSigninUI = function(opt_email) { |
| 274 $('add-user-button').hidden = true; |
| 275 $('cancel-add-user-button').hidden = false; |
| 276 $('add-user-header-bar-item').hidden = $('pod-row').pods.length == 0; |
| 277 chrome.send('showAddUser', [opt_email]); |
| 278 }; |
| 279 |
| 280 /** |
| 281 * Resets sign-in input fields. |
| 282 */ |
| 283 DisplayManager.resetSigninUI = function() { |
| 284 var currentScreenId = Oobe.getInstance().currentScreen.id; |
| 285 |
| 286 if (localStrings.getString('authType') == 'webui') |
| 287 $(SCREEN_SIGNIN).reset(currentScreenId == SCREEN_SIGNIN); |
| 288 else |
| 289 $(SCREEN_GAIA_SIGNIN).reset(currentScreenId == SCREEN_GAIA_SIGNIN); |
| 290 |
| 291 $('pod-row').reset(currentScreenId == SCREEN_ACCOUNT_PICKER); |
| 292 }; |
| 293 |
| 294 /** |
| 295 * Shows sign-in error bubble. |
| 296 * @param {number} loginAttempts Number of login attemps tried. |
| 297 * @param {string} message Error message to show. |
| 298 * @param {string} link Text to use for help link. |
| 299 * @param {number} helpId Help topic Id associated with help link. |
| 300 */ |
| 301 DisplayManager.showSignInError = function(loginAttempts, message, link, |
| 302 helpId) { |
| 303 var currentScreenId = Oobe.getInstance().currentScreen.id; |
| 304 var anchor = undefined; |
| 305 var anchorPos = undefined; |
| 306 if (currentScreenId == SCREEN_SIGNIN) { |
| 307 anchor = $('email'); |
| 308 |
| 309 // Show email field so that bubble shows up at the right location. |
| 310 $(SCREEN_SIGNIN).reset(true); |
| 311 } else if (currentScreenId == SCREEN_GAIA_SIGNIN) { |
| 312 // Use anchorPos since we won't be able to get the input fields of Gaia. |
| 313 anchorPos = DisplayManager.getOffset(Oobe.getInstance().currentScreen); |
| 314 |
| 315 // Ideally, we should just use |
| 316 // anchorPos = DisplayManager.getOffset($('signin-frame')); |
| 317 // to get a good anchor point. However, this always gives (0,0) on |
| 318 // the device. |
| 319 // TODO(xiyuan): Figure out why the above fails and get rid of this. |
| 320 anchorPos.left += 150; // (640 - 340) / 2 |
| 321 |
| 322 // TODO(xiyuan): Find a reliable way to align with Gaia UI. |
| 323 anchorPos.left += 60; |
| 324 anchorPos.top += 105; |
| 325 } else if (currentScreenId == SCREEN_ACCOUNT_PICKER && |
| 326 $('pod-row').activated) { |
| 327 const MAX_LOGIN_ATTEMMPTS_IN_POD = 3; |
| 328 if (loginAttempts > MAX_LOGIN_ATTEMMPTS_IN_POD) { |
| 329 Oobe.showSigninUI($('pod-row').activated.user.emailAddress); |
| 330 return; |
| 331 } |
| 332 |
| 333 anchor = $('pod-row').activated.mainInput; |
| 334 } |
| 335 if (!anchor && !anchorPos) { |
| 336 console.log('Warning: Failed to find anchor for error :' + |
| 337 message); |
| 338 return; |
| 339 } |
| 340 |
| 341 var error = document.createElement('div'); |
| 342 |
| 343 var messageDiv = document.createElement('div'); |
| 344 messageDiv.className = 'error-message'; |
| 345 messageDiv.textContent = message; |
| 346 error.appendChild(messageDiv); |
| 347 |
| 348 if (link) { |
| 349 messageDiv.classList.add('error-message-padding'); |
| 350 |
| 351 var helpLink = document.createElement('a'); |
| 352 helpLink.href = '#'; |
| 353 helpLink.textContent = link; |
| 354 helpLink.onclick = function(e) { |
| 355 chrome.send('launchHelpApp', [helpId]); |
| 356 } |
| 357 error.appendChild(helpLink); |
| 358 } |
| 359 |
| 360 if (anchor) |
| 361 $('bubble').showContentForElement(anchor, error); |
| 362 else if (anchorPos) |
| 363 $('bubble').showContentAt(anchorPos.left, anchorPos.top, error); |
| 364 }; |
| 365 |
| 366 /** |
| 367 * Clears error bubble. |
| 368 */ |
| 369 DisplayManager.clearErrors = function() { |
| 370 $('bubble').hide(); |
| 371 }; |
| 372 |
| 373 /** |
| 374 * Sets text content for a div with |labelId|. |
| 375 * @param {string} labelId Id of the label div. |
| 376 * @param {string} labelText Text for the label. |
| 377 */ |
| 378 DisplayManager.setLabelText = function(labelId, labelText) { |
| 379 $(labelId).textContent = labelText; |
| 380 }; |
| 381 |
| 382 // Export |
| 383 return { |
| 384 DisplayManager: DisplayManager |
| 385 }; |
| 386 }); |
OLD | NEW |