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 <include src="../uber/uber_utils.js"> 6 7 cr.define('help', function() { 8 /** 9 * Encapsulated handling of the help page. 10 */ 11 function HelpPage() {} 12 13 cr.addSingletonGetter(HelpPage); 14 15 HelpPage.prototype = { 16 __proto__: HTMLDivElement.prototype, 17 18 /** 19 * True if after update powerwash button should be displayed. 20 * @private 21 */ 22 powerwashAfterUpdate_: false, 23 24 /** 25 * List of the channels names. 26 * @private 27 */ 28 channelList_: ['dev-channel', 'beta-channel', 'stable-channel'], 29 30 /** 31 * Bubble for error messages and notifications. 32 * @private 33 */ 34 bubble_: null, 35 36 /** 37 * Name of the channel the device is currently on. 38 * @private 39 */ 40 currentChannel_: null, 41 42 /** 43 * Name of the channel the device is supposed to be on. 44 * @private 45 */ 46 targetChannel_: null, 47 48 /** 49 * Perform initial setup. 50 */ 51 initialize: function() { 52 var self = this; 53 54 uber.onContentFrameLoaded(); 55 56 // Set the title. 57 var title = loadTimeData.getString('helpTitle'); 58 uber.invokeMethodOnParent('setTitle', {title: title}); 59 60 $('product-license').innerHTML = loadTimeData.getString('productLicense'); 61 if (cr.isChromeOS) { 62 $('product-os-license').innerHTML = 63 loadTimeData.getString('productOsLicense'); 64 } 65 66 var productTOS = $('product-tos'); 67 if (productTOS) 68 productTOS.innerHTML = loadTimeData.getString('productTOS'); 69 70 $('get-help').onclick = function() { 71 chrome.send('openHelpPage'); 72 }; 73 $('report-issue').onclick = function() { 74 chrome.send('openFeedbackDialog'); 75 }; 76 77 this.maybeSetOnClick_($('more-info-expander'), 78 this.toggleMoreInfo_.bind(this)); 79 80 this.maybeSetOnClick_($('promote'), function() { 81 chrome.send('promoteUpdater'); 82 }); 83 this.maybeSetOnClick_($('relaunch'), function() { 84 chrome.send('relaunchNow'); 85 }); 86 if (cr.isChromeOS) { 87 this.maybeSetOnClick_($('relaunch-and-powerwash'), function() { 88 chrome.send('relaunchAndPowerwash'); 89 }); 90 91 this.channelTable_ = { 92 'stable-channel': { 93 'name': loadTimeData.getString('stable'), 94 'label': loadTimeData.getString('currentChannelStable'), 95 }, 96 'beta-channel': { 97 'name': loadTimeData.getString('beta'), 98 'label': loadTimeData.getString('currentChannelBeta') 99 }, 100 'dev-channel': { 101 'name': loadTimeData.getString('dev'), 102 'label': loadTimeData.getString('currentChannelDev') 103 } 104 }; 105 } 106 107 var channelChanger = $('channel-changer'); 108 if (channelChanger) { 109 channelChanger.onchange = function(event) { 110 self.setChannel_(event.target.value, false); 111 }; 112 } 113 114 if (cr.isChromeOS) { 115 cr.ui.overlay.setupOverlay($('overlay-container')); 116 cr.ui.overlay.globalInitialization(); 117 $('overlay-container').addEventListener('cancelOverlay', function() { 118 self.showOverlay_(null); 119 }); 120 $('change-channel').onclick = function() { 121 self.showOverlay_($('channel-change-page')); 122 }; 123 124 var channelChangeDisallowedError = document.createElement('div'); 125 channelChangeDisallowedError.className = 'channel-change-error-bubble'; 126 127 var channelChangeDisallowedIcon = document.createElement('div'); 128 channelChangeDisallowedIcon.classList.add('help-page-icon-large'); 129 channelChangeDisallowedIcon.classList.add('channel-change-error-icon'); 130 channelChangeDisallowedError.appendChild(channelChangeDisallowedIcon); 131 132 var channelChangeDisallowedText = document.createElement('div'); 133 channelChangeDisallowedText.className = 'channel-change-error-text'; 134 channelChangeDisallowedText.textContent = 135 loadTimeData.getString('channelChangeDisallowedMessage'); 136 channelChangeDisallowedError.appendChild(channelChangeDisallowedText); 137 138 $('channel-change-disallowed-icon').onclick = function() { 139 self.showBubble_(channelChangeDisallowedError, 140 $('help-container'), 141 $('channel-change-disallowed-icon'), 142 cr.ui.ArrowLocation.TOP_END); 143 }; 144 } 145 146 cr.ui.FocusManager.disableMouseFocusOnButtons(); 147 148 // Attempt to update. 149 chrome.send('onPageLoaded'); 150 }, 151 152 /** 153 * Shows the bubble. 154 * @param {HTMLDivElement} content The content of the bubble. 155 * @param {HTMLElement} target The element at which the bubble points. 156 * @param {HTMLElement} domSibling The element after which the bubble is 157 * added to the DOM. 158 * @param {cr.ui.ArrowLocation} location The arrow location. 159 * @private 160 */ 161 showBubble_: function(content, domSibling, target, location) { 162 if (!cr.isChromeOS) 163 return; 164 this.hideBubble_(); 165 var bubble = new cr.ui.AutoCloseBubble; 166 bubble.anchorNode = target; 167 bubble.domSibling = domSibling; 168 bubble.arrowLocation = location; 169 bubble.content = content; 170 bubble.show(); 171 this.bubble_ = bubble; 172 }, 173 174 /** 175 * Hides the bubble. 176 * @private 177 */ 178 hideBubble_: function() { 179 if (!cr.isChromeOS) 180 return; 181 if (this.bubble_) 182 this.bubble_.hide(); 183 }, 184 185 /** 186 * Toggles the visible state of the 'More Info' section. 187 * @private 188 */ 189 toggleMoreInfo_: function() { 190 var moreInfo = $('more-info-container'); 191 var visible = moreInfo.className == 'visible'; 192 moreInfo.className = visible ? '' : 'visible'; 193 moreInfo.style.height = visible ? '' : moreInfo.scrollHeight + 'px'; 194 moreInfo.addEventListener('webkitTransitionEnd', function(event) { 195 $('more-info-expander').textContent = visible ? 196 loadTimeData.getString('showMoreInfo') : 197 loadTimeData.getString('hideMoreInfo'); 198 }); 199 }, 200 201 /** 202 * Assigns |method| to the onclick property of |el| if |el| exists. 203 * @private 204 */ 205 maybeSetOnClick_: function(el, method) { 206 if (el) 207 el.onclick = method; 208 }, 209 210 /** 211 * @private 212 */ 213 setUpdateImage_: function(state) { 214 $('update-status-icon').className = 'help-page-icon ' + state; 215 }, 216 217 /** 218 * Returns current overlay. 219 * @return {HTMLElement} Current overlay 220 * @private 221 */ 222 getCurrentOverlay_: function() { 223 return document.querySelector('#overlay .page.showing'); 224 }, 225 226 /** 227 * @return {boolean} True, if new channel switcher UI is used, 228 * false otherwise. 229 * @private 230 */ 231 isNewChannelSwitcherUI_: function() { 232 return !loadTimeData.valueExists('disableNewChannelSwitcherUI'); 233 }, 234 235 /** 236 * @return {boolean} True if target and current channels are not 237 * null and not equals 238 * @private 239 */ 240 channelsDiffer_: function() { 241 var current = this.currentChannel_; 242 var target = this.targetChannel_; 243 return (current != null && target != null && current != target); 244 }, 245 246 /** 247 * @private 248 */ 249 setUpdateStatus_: function(status, message) { 250 var channel = this.targetChannel_; 251 if (status == 'checking') { 252 this.setUpdateImage_('working'); 253 $('update-status-message').innerHTML = 254 loadTimeData.getString('updateCheckStarted'); 255 } else if (status == 'updating') { 256 this.setUpdateImage_('working'); 257 if (this.channelsDiffer_()) { 258 $('update-status-message').innerHTML = 259 loadTimeData.getStringF('updatingChannelSwitch', 260 this.channelTable_[channel].label); 261 } else { 262 $('update-status-message').innerHTML = 263 loadTimeData.getStringF('updating'); 264 } 265 } else if (status == 'nearly_updated') { 266 this.setUpdateImage_('up-to-date'); 267 if (this.channelsDiffer_()) { 268 $('update-status-message').innerHTML = 269 loadTimeData.getString('successfulChannelSwitch'); 270 } else { 271 $('update-status-message').innerHTML = 272 loadTimeData.getString('updateAlmostDone'); 273 } 274 } else if (status == 'updated') { 275 this.setUpdateImage_('up-to-date'); 276 $('update-status-message').innerHTML = 277 loadTimeData.getString('upToDate'); 278 } else if (status == 'failed') { 279 this.setUpdateImage_('failed'); 280 $('update-status-message').innerHTML = message; 281 } 282 283 var container = $('update-status-container'); 284 if (container) { 285 container.hidden = status == 'disabled'; 286 $('relaunch').hidden = status != 'nearly_updated'; 287 288 if (!cr.isMac) 289 $('update-percentage').hidden = status != 'updating'; 290 } 291 292 if ($('relaunch-and-powerwash')) { 293 // It's allowed to do powerwash only for customer devices, 294 // when user explicitly decides to update to a more stable 295 // channel. 296 $('relaunch-and-powerwash').hidden = 297 !this.powerwashAfterUpdate_ || status != 'nearly_updated'; 298 } 299 }, 300 301 /** 302 * @private 303 */ 304 setProgress_: function(progress) { 305 $('update-percentage').innerHTML = progress + '%'; 306 }, 307 308 /** 309 * @private 310 */ 311 setAllowedConnectionTypesMsg_: function(message) { 312 $('allowed-connection-types-message').innerText = message; 313 }, 314 315 /** 316 * @private 317 */ 318 showAllowedConnectionTypesMsg_: function(visible) { 319 $('allowed-connection-types-message').hidden = !visible; 320 }, 321 322 /** 323 * @private 324 */ 325 setPromotionState_: function(state) { 326 if (state == 'hidden') { 327 $('promote').hidden = true; 328 } else if (state == 'enabled') { 329 $('promote').disabled = false; 330 $('promote').hidden = false; 331 } else if (state == 'disabled') { 332 $('promote').disabled = true; 333 $('promote').hidden = false; 334 } 335 }, 336 337 /** 338 * @private 339 */ 340 setOSVersion_: function(version) { 341 if (!cr.isChromeOS) 342 console.error('OS version unsupported on non-CrOS'); 343 344 $('os-version').parentNode.hidden = (version == ''); 345 $('os-version').textContent = version; 346 }, 347 348 /** 349 * @private 350 */ 351 setOSFirmware_: function(firmware) { 352 if (!cr.isChromeOS) 353 console.error('OS firmware unsupported on non-CrOS'); 354 355 $('firmware').parentNode.hidden = (firmware == ''); 356 $('firmware').textContent = firmware; 357 }, 358 359 /** 360 * Sets the given overlay to show. This hides whatever overlay is currently 361 * showing, if any. 362 * @param {HTMLElement} node The overlay page to show. If null, all 363 * overlays are hidden. 364 */ 365 showOverlay_: function(node) { 366 var currentlyShowingOverlay = this.getCurrentOverlay_(); 367 if (currentlyShowingOverlay) 368 currentlyShowingOverlay.classList.remove('showing'); 369 if (node) 370 node.classList.add('showing'); 371 $('overlay-container').hidden = !node; 372 }, 373 374 /** 375 * Updates name of the current channel, i.e. the name of the 376 * channel the device is currently on. 377 * @param {string} channel The name of the current channel 378 * @private 379 */ 380 updateCurrentChannel_: function(channel) { 381 if (this.channelList_.indexOf(channel) < 0) 382 return; 383 $('current-channel').textContent = loadTimeData.getStringF( 384 'currentChannel', this.channelTable_[channel].label); 385 this.currentChannel_ = channel; 386 help.ChannelChangePage.updateCurrentChannel(channel); 387 }, 388 389 /** 390 * |enabled| is true if the release channel can be enabled. 391 * @private 392 */ 393 updateEnableReleaseChannel_: function(enabled) { 394 this.updateChannelChangerContainerVisibility_(enabled); 395 $('change-channel').disabled = !enabled; 396 $('channel-change-disallowed-icon').hidden = enabled; 397 }, 398 399 /** 400 * Sets the device target channel. 401 * @param {string} channel The name of the target channel 402 * @param {boolean} isPowerwashAllowed True iff powerwash is allowed 403 * @private 404 */ 405 setChannel_: function(channel, isPowerwashAllowed) { 406 this.powerwashAfterUpdate_ = isPowerwashAllowed; 407 this.targetChannel_ = channel; 408 chrome.send('setChannel', [channel, isPowerwashAllowed]); 409 $('channel-change-confirmation').hidden = false; 410 $('channel-change-confirmation').textContent = loadTimeData.getStringF( 411 'channel-changed', this.channelTable_[channel].name); 412 }, 413 414 /** 415 * Sets the value of the "Build Date" field of the "More Info" section. 416 * @param {string} buildDate The date of the build. 417 * @private 418 */ 419 setBuildDate_: function(buildDate) { 420 $('build-date-container').classList.remove('empty'); 421 $('build-date').textContent = buildDate; 422 }, 423 424 /** 425 * Updates channel-change-page-container visibility according to 426 * internal state. 427 * @private 428 */ 429 updateChannelChangePageContainerVisibility_: function() { 430 if (!this.isNewChannelSwitcherUI_()) { 431 $('channel-change-page-container').hidden = true; 432 return; 433 } 434 $('channel-change-page-container').hidden = 435 !help.ChannelChangePage.isPageReady(); 436 }, 437 438 /** 439 * Updates channel-changer dropdown visibility if |visible| is 440 * true and new channel switcher UI is disallowed. 441 * @param {boolean} visible True if channel-changer should be 442 * displayed, false otherwise. 443 * @private 444 */ 445 updateChannelChangerContainerVisibility_: function(visible) { 446 if (this.isNewChannelSwitcherUI_()) { 447 $('channel-changer').hidden = true; 448 return; 449 } 450 $('channel-changer').hidden = !visible; 451 }, 452 }; 453 454 HelpPage.setUpdateStatus = function(status, message) { 455 HelpPage.getInstance().setUpdateStatus_(status, message); 456 }; 457 458 HelpPage.setProgress = function(progress) { 459 HelpPage.getInstance().setProgress_(progress); 460 }; 461 462 HelpPage.setAndShowAllowedConnectionTypesMsg = function(message) { 463 HelpPage.getInstance().setAllowedConnectionTypesMsg_(message); 464 HelpPage.getInstance().showAllowedConnectionTypesMsg_(true); 465 }; 466 467 HelpPage.showAllowedConnectionTypesMsg = function(visible) { 468 HelpPage.getInstance().showAllowedConnectionTypesMsg_(visible); 469 }; 470 471 HelpPage.setPromotionState = function(state) { 472 HelpPage.getInstance().setPromotionState_(state); 473 }; 474 475 HelpPage.setObsoleteOS = function(obsolete) { 476 HelpPage.getInstance().setObsoleteOS_(obsolete); 477 }; 478 479 HelpPage.setOSVersion = function(version) { 480 HelpPage.getInstance().setOSVersion_(version); 481 }; 482 483 HelpPage.setOSFirmware = function(firmware) { 484 HelpPage.getInstance().setOSFirmware_(firmware); 485 }; 486 487 HelpPage.showOverlay = function(node) { 488 HelpPage.getInstance().showOverlay_(node); 489 }; 490 491 HelpPage.cancelOverlay = function() { 492 HelpPage.getInstance().showOverlay_(null); 493 }; 494 495 HelpPage.updateIsEnterpriseManaged = function(isEnterpriseManaged) { 496 if (!cr.isChromeOS) 497 return; 498 help.ChannelChangePage.updateIsEnterpriseManaged(isEnterpriseManaged); 499 }; 500 501 HelpPage.updateCurrentChannel = function(channel) { 502 if (!cr.isChromeOS) 503 return; 504 HelpPage.getInstance().updateCurrentChannel_(channel); 505 }; 506 507 HelpPage.updateTargetChannel = function(channel) { 508 if (!cr.isChromeOS) 509 return; 510 help.ChannelChangePage.updateTargetChannel(channel); 511 }; 512 513 HelpPage.updateEnableReleaseChannel = function(enabled) { 514 HelpPage.getInstance().updateEnableReleaseChannel_(enabled); 515 }; 516 517 HelpPage.setChannel = function(channel, isPowerwashAllowed) { 518 HelpPage.getInstance().setChannel_(channel, isPowerwashAllowed); 519 }; 520 521 HelpPage.setBuildDate = function(buildDate) { 522 HelpPage.getInstance().setBuildDate_(buildDate); 523 }; 524 525 HelpPage.updateChannelChangePageContainerVisibility = function() { 526 HelpPage.getInstance().updateChannelChangePageContainerVisibility_(); 527 }; 528 529 // Export 530 return { 531 HelpPage: HelpPage 532 }; 533 }); 534 535 /** 536 * onload listener to initialize the HelpPage. 537 */ 538 window.onload = function() { 539 help.HelpPage.getInstance().initialize(); 540 if (cr.isChromeOS) 541 help.ChannelChangePage.getInstance().initialize(); 542 }; 543