Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 cr.define('options', function() { | 5 cr.define('options', function() { |
| 6 ///////////////////////////////////////////////////////////////////////////// | 6 ///////////////////////////////////////////////////////////////////////////// |
| 7 // OptionsPage class: | 7 // OptionsPage class: |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Base class for options page. | 10 * Base class for options page. |
| 11 * @constructor | 11 * @constructor |
| 12 * @param {string} name Options page name. | 12 * @param {string} name Options page name. |
| 13 * @param {string} title Options page title, used for navigation bar. | 13 * @param {string} title Options page title, used for navigation bar. |
| 14 * @extends {EventTarget} | 14 * @extends {EventTarget} |
| 15 */ | 15 */ |
| 16 function OptionsPage(name, title, pageDivName) { | 16 function OptionsPage(name, title, pageDivName) { |
| 17 this.name = name; | 17 this.name = name; |
| 18 this.title = title; | 18 this.title = title; |
| 19 this.pageDivName = pageDivName; | 19 this.pageDivName = pageDivName; |
| 20 this.pageDiv = $(this.pageDivName); | 20 this.pageDiv = $(this.pageDivName); |
| 21 this.tab = null; | 21 this.tab = null; |
| 22 } | 22 } |
| 23 | 23 |
| 24 const SUBPAGE_SHEET_COUNT = 1; | |
| 25 | |
| 26 const HORIZONTAL_OFFSET = 155; | 24 const HORIZONTAL_OFFSET = 155; |
| 27 | 25 |
| 28 /** | 26 /** |
| 29 * This is the absolute difference maintained between standard and | 27 * This is the absolute difference maintained between standard and |
| 30 * fixed-width font sizes. Refer http://crbug.com/91922. | 28 * fixed-width font sizes. Refer http://crbug.com/91922. |
| 31 */ | 29 */ |
| 32 OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD = 3; | 30 OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD = 3; |
| 33 | 31 |
| 34 /** | 32 /** |
| 35 * Main level option pages. Maps lower-case page names to the respective page | 33 * Main level option pages. Maps lower-case page names to the respective page |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 /** | 66 /** |
| 69 * "Navigates" to a page, meaning that the page will be shown and the | 67 * "Navigates" to a page, meaning that the page will be shown and the |
| 70 * appropriate entry is placed in the history. | 68 * appropriate entry is placed in the history. |
| 71 * @param {string} pageName Page name. | 69 * @param {string} pageName Page name. |
| 72 */ | 70 */ |
| 73 OptionsPage.navigateToPage = function(pageName) { | 71 OptionsPage.navigateToPage = function(pageName) { |
| 74 this.showPageByName(pageName, true); | 72 this.showPageByName(pageName, true); |
| 75 }; | 73 }; |
| 76 | 74 |
| 77 /** | 75 /** |
| 78 * Shows a registered page. This handles both top-level pages and sub-pages. | 76 * Shows a registered page. |
|
Dan Beam
2012/03/06 00:31:41
nit: or dialog/overlay.
csilv
2012/03/06 01:03:04
Done.
| |
| 79 * @param {string} pageName Page name. | 77 * @param {string} pageName Page name. |
| 80 * @param {boolean} updateHistory True if we should update the history after | 78 * @param {boolean} updateHistory True if we should update the history after |
| 81 * showing the page. | 79 * showing the page. |
| 82 * @private | 80 * @private |
| 83 */ | 81 */ |
| 84 OptionsPage.showPageByName = function(pageName, updateHistory) { | 82 OptionsPage.showPageByName = function(pageName, updateHistory) { |
| 85 // Find the currently visible root-level page. | 83 // Find the currently visible root-level page. |
| 86 var rootPage = null; | 84 var rootPage = null; |
| 87 for (var name in this.registeredPages) { | 85 for (var name in this.registeredPages) { |
| 88 var page = this.registeredPages[name]; | 86 var page = this.registeredPages[name]; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 102 return; | 100 return; |
| 103 } else { | 101 } else { |
| 104 targetPage = this.getDefaultPage(); | 102 targetPage = this.getDefaultPage(); |
| 105 } | 103 } |
| 106 } | 104 } |
| 107 | 105 |
| 108 pageName = targetPage.name.toLowerCase(); | 106 pageName = targetPage.name.toLowerCase(); |
| 109 var targetPageWasVisible = targetPage.visible; | 107 var targetPageWasVisible = targetPage.visible; |
| 110 | 108 |
| 111 // Determine if the root page is 'sticky', meaning that it | 109 // Determine if the root page is 'sticky', meaning that it |
| 112 // shouldn't change when showing a sub-page. This can happen for special | 110 // shouldn't change when showing an overlay. This can happen for special |
| 113 // pages like Search. | 111 // pages like Search. |
| 114 var isRootPageLocked = | 112 var isRootPageLocked = |
| 115 rootPage && rootPage.sticky && targetPage.parentPage; | 113 rootPage && rootPage.sticky && targetPage.parentPage; |
| 116 | 114 |
| 117 // Notify pages if they will be hidden. | 115 // Notify pages if they will be hidden. |
| 118 for (var name in this.registeredPages) { | 116 for (var name in this.registeredPages) { |
| 119 var page = this.registeredPages[name]; | 117 var page = this.registeredPages[name]; |
| 120 if (!page.parentPage && isRootPageLocked) | 118 if (!page.parentPage && isRootPageLocked) |
| 121 continue; | 119 continue; |
| 122 if (page.willHidePage && name != pageName && | 120 if (page.willHidePage && name != pageName && |
| 123 !page.isAncestorOfPage(targetPage)) { | 121 !page.isAncestorOfPage(targetPage)) { |
| 124 page.willHidePage(); | 122 page.willHidePage(); |
| 125 } | 123 } |
| 126 } | 124 } |
| 127 | 125 |
| 128 // Update visibilities to show only the hierarchy of the target page. | 126 // Update visibilities to show only the hierarchy of the target page. |
| 129 for (var name in this.registeredPages) { | 127 for (var name in this.registeredPages) { |
| 130 var page = this.registeredPages[name]; | 128 var page = this.registeredPages[name]; |
| 131 if (!page.parentPage && isRootPageLocked) | 129 if (!page.parentPage && isRootPageLocked) |
| 132 continue; | 130 continue; |
| 133 page.visible = name == pageName || | 131 page.visible = name == pageName || page.isAncestorOfPage(targetPage); |
| 134 (!document.documentElement.classList.contains('hide-menu') && | |
| 135 page.isAncestorOfPage(targetPage)); | |
| 136 } | 132 } |
| 137 | 133 |
| 138 // Update the history and current location. | 134 // Update the history and current location. |
| 139 if (updateHistory) | 135 if (updateHistory) |
| 140 this.updateHistoryState_(); | 136 this.updateHistoryState_(); |
| 141 | 137 |
| 142 // Update tab title. | 138 // Update tab title. |
| 143 this.setTitle_(targetPage.title); | 139 this.setTitle_(targetPage.title); |
| 144 | 140 |
| 145 // Notify pages if they were shown. | 141 // Notify pages if they were shown. |
| 146 for (var name in this.registeredPages) { | 142 for (var name in this.registeredPages) { |
| 147 var page = this.registeredPages[name]; | 143 var page = this.registeredPages[name]; |
| 148 if (!page.parentPage && isRootPageLocked) | 144 if (!page.parentPage && isRootPageLocked) |
| 149 continue; | 145 continue; |
| 150 if (!targetPageWasVisible && page.didShowPage && | 146 if (!targetPageWasVisible && page.didShowPage && |
| 151 (name == pageName || page.isAncestorOfPage(targetPage))) { | 147 (name == pageName || page.isAncestorOfPage(targetPage))) { |
| 152 page.didShowPage(); | 148 page.didShowPage(); |
| 153 } | 149 } |
| 154 } | 150 } |
| 155 }; | 151 }; |
| 156 | 152 |
| 157 /** | 153 /** |
| 158 * Updates the parts of the UI necessary for correctly hiding or displaying | |
| 159 * subpages. | |
| 160 * @private | |
| 161 */ | |
| 162 OptionsPage.updateDisplayForShowOrHideSubpage_ = function() { | |
| 163 OptionsPage.updateSubpageBackdrop_(); | |
| 164 OptionsPage.updateAriaHiddenForPages_(); | |
| 165 OptionsPage.updateScrollPosition_(); | |
| 166 }; | |
| 167 | |
| 168 /** | |
| 169 * Sets the aria-hidden attribute for pages which have been 'overlapped' by a | |
| 170 * sub-page, and removes aria-hidden from the topmost page or subpage. | |
| 171 * @private | |
| 172 */ | |
| 173 OptionsPage.updateAriaHiddenForPages_ = function() { | |
|
Dan Beam
2012/03/06 00:31:41
don't we want to do this for overlays?
csilv
2012/03/06 01:03:04
Perhaps, but that should probably be in overlay.js
Dan Beam
2012/03/06 01:57:37
I see what you mean, but it seems reasonable to do
| |
| 174 var visiblePages = OptionsPage.getVisiblePages_(); | |
| 175 | |
| 176 // |visiblePages| is empty when switching top-level pages. | |
| 177 if (!visiblePages.length) | |
| 178 return; | |
| 179 | |
| 180 var topmostPage = visiblePages.pop(); | |
| 181 | |
| 182 for (var i = 0; i < visiblePages.length; ++i) { | |
| 183 var page = visiblePages[i]; | |
| 184 var nestingLevel = page.nestingLevel; | |
| 185 var container = nestingLevel > 0 ? | |
| 186 $('subpage-sheet-container-' + nestingLevel) : $('page-container'); | |
| 187 container.setAttribute('aria-hidden', true); | |
| 188 } | |
| 189 | |
| 190 var topmostPageContainer = topmostPage.nestingLevel > 0 ? | |
| 191 $('subpage-sheet-container-' + topmostPage.nestingLevel) : | |
| 192 $('page-container'); | |
| 193 topmostPageContainer.removeAttribute('aria-hidden'); | |
| 194 }; | |
| 195 | |
| 196 /** | |
| 197 * Sets the title of the page. This is accomplished by calling into the | 154 * Sets the title of the page. This is accomplished by calling into the |
| 198 * parent page API. | 155 * parent page API. |
| 199 * @param {String} title The title string. | 156 * @param {String} title The title string. |
| 200 * @private | 157 * @private |
| 201 */ | 158 */ |
| 202 OptionsPage.setTitle_ = function(title) { | 159 OptionsPage.setTitle_ = function(title) { |
| 203 uber.invokeMethodOnParent('setTitle', {title: title}); | 160 uber.invokeMethodOnParent('setTitle', {title: title}); |
| 204 }; | 161 }; |
| 205 | 162 |
| 206 /** | 163 /** |
| 207 * Updates the visibility and stacking order of the subpage backdrop | 164 * Scrolls the page to the correct position (the top when opening an overlay, |
| 208 * according to which subpage is topmost and visible. | 165 * or the old scroll position a previously hidden overlay becomes visible). |
| 209 * @private | |
| 210 */ | |
| 211 OptionsPage.updateSubpageBackdrop_ = function() { | |
| 212 var topmostPage = OptionsPage.getTopmostVisibleNonOverlayPage_(); | |
| 213 var nestingLevel = topmostPage ? topmostPage.nestingLevel : 0; | |
| 214 | |
| 215 var subpageBackdrop = $('subpage-backdrop'); | |
| 216 if (nestingLevel > 0) { | |
| 217 var container = $('subpage-sheet-container-' + nestingLevel); | |
| 218 subpageBackdrop.style.zIndex = | |
| 219 parseInt(window.getComputedStyle(container).zIndex) - 1; | |
| 220 subpageBackdrop.hidden = false; | |
| 221 } else { | |
| 222 subpageBackdrop.hidden = true; | |
| 223 } | |
| 224 }; | |
| 225 | |
| 226 /** | |
| 227 * Scrolls the page to the correct position (the top when opening a subpage, | |
| 228 * or the old scroll position a previously hidden subpage becomes visible). | |
| 229 * @private | 166 * @private |
| 230 */ | 167 */ |
| 231 OptionsPage.updateScrollPosition_ = function() { | 168 OptionsPage.updateScrollPosition_ = function() { |
| 232 var topmostPage = OptionsPage.getTopmostVisibleNonOverlayPage_(); | 169 var container = $('page-container'); |
| 233 var nestingLevel = topmostPage ? topmostPage.nestingLevel : 0; | |
| 234 | |
| 235 var container = (nestingLevel > 0) ? | |
| 236 $('subpage-sheet-container-' + nestingLevel) : $('page-container'); | |
| 237 | |
| 238 var scrollTop = container.oldScrollTop || 0; | 170 var scrollTop = container.oldScrollTop || 0; |
| 239 container.oldScrollTop = undefined; | 171 container.oldScrollTop = undefined; |
| 240 window.scroll(document.body.scrollLeft, scrollTop); | 172 window.scroll(document.body.scrollLeft, scrollTop); |
| 241 }; | 173 }; |
| 242 | 174 |
| 243 /** | 175 /** |
| 244 * Pushes the current page onto the history stack, overriding the last page | 176 * Pushes the current page onto the history stack, overriding the last page |
| 245 * if it is the generic chrome://settings/. | 177 * if it is the generic chrome://settings/. |
| 246 * @private | 178 * @private |
| 247 */ | 179 */ |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 379 /** | 311 /** |
| 380 * Returns the topmost visible page, or null if no page is visible. | 312 * Returns the topmost visible page, or null if no page is visible. |
| 381 * @return {OptionPage} The topmost visible page. | 313 * @return {OptionPage} The topmost visible page. |
| 382 */ | 314 */ |
| 383 OptionsPage.getTopmostVisiblePage = function() { | 315 OptionsPage.getTopmostVisiblePage = function() { |
| 384 // Check overlays first since they're top-most if visible. | 316 // Check overlays first since they're top-most if visible. |
| 385 return this.getVisibleOverlay_() || this.getTopmostVisibleNonOverlayPage_(); | 317 return this.getVisibleOverlay_() || this.getTopmostVisibleNonOverlayPage_(); |
| 386 }; | 318 }; |
| 387 | 319 |
| 388 /** | 320 /** |
| 389 * Closes the topmost open subpage, if any. | |
| 390 * @private | |
| 391 */ | |
| 392 OptionsPage.closeTopSubPage_ = function() { | |
| 393 var topPage = this.getTopmostVisiblePage(); | |
| 394 if (topPage && !topPage.isOverlay && topPage.parentPage) { | |
| 395 if (topPage.willHidePage) | |
| 396 topPage.willHidePage(); | |
| 397 topPage.visible = false; | |
| 398 } | |
| 399 | |
| 400 this.updateHistoryState_(); | |
| 401 }; | |
| 402 | |
| 403 /** | |
| 404 * Closes all subpages below the given level. | |
| 405 * @param {number} level The nesting level to close below. | |
| 406 */ | |
| 407 OptionsPage.closeSubPagesToLevel = function(level) { | |
| 408 var topPage = this.getTopmostVisiblePage(); | |
| 409 while (topPage && topPage.nestingLevel > level) { | |
| 410 if (topPage.willHidePage) | |
| 411 topPage.willHidePage(); | |
| 412 topPage.visible = false; | |
| 413 topPage = topPage.parentPage; | |
| 414 } | |
| 415 | |
| 416 this.updateHistoryState_(); | |
| 417 }; | |
| 418 | |
| 419 /** | |
| 420 * Updates managed banner visibility state based on the topmost page. | 321 * Updates managed banner visibility state based on the topmost page. |
| 421 */ | 322 */ |
| 422 OptionsPage.updateManagedBannerVisibility = function() { | 323 OptionsPage.updateManagedBannerVisibility = function() { |
| 423 var topPage = this.getTopmostVisiblePage(); | 324 var topPage = this.getTopmostVisiblePage(); |
| 424 if (topPage) | 325 if (topPage) |
| 425 topPage.updateManagedBannerVisibility(); | 326 topPage.updateManagedBannerVisibility(); |
| 426 }; | 327 }; |
| 427 | 328 |
| 428 /** | 329 /** |
| 429 * Shows the tab contents for the given navigation tab. | 330 * Shows the tab contents for the given navigation tab. |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 475 */ | 376 */ |
| 476 OptionsPage.findSectionForNode_ = function(node) { | 377 OptionsPage.findSectionForNode_ = function(node) { |
| 477 while (node = node.parentNode) { | 378 while (node = node.parentNode) { |
| 478 if (node.nodeName == 'SECTION') | 379 if (node.nodeName == 'SECTION') |
| 479 return node; | 380 return node; |
| 480 } | 381 } |
| 481 return null; | 382 return null; |
| 482 }; | 383 }; |
| 483 | 384 |
| 484 /** | 385 /** |
| 485 * Registers a new Sub-page. | |
| 486 * @param {OptionsPage} subPage Sub-page to register. | |
| 487 * @param {OptionsPage} parentPage Associated parent page for this page. | |
| 488 * @param {Array} associatedControls Array of control elements that lead to | |
| 489 * this sub-page. The first item is typically a button in a root-level | |
| 490 * page. There may be additional buttons for nested sub-pages. | |
| 491 */ | |
| 492 OptionsPage.registerSubPage = function(subPage, | |
| 493 parentPage, | |
| 494 associatedControls) { | |
| 495 this.registeredPages[subPage.name.toLowerCase()] = subPage; | |
| 496 subPage.parentPage = parentPage; | |
| 497 if (associatedControls) { | |
| 498 subPage.associatedControls = associatedControls; | |
| 499 if (associatedControls.length) { | |
| 500 subPage.associatedSection = | |
| 501 this.findSectionForNode_(associatedControls[0]); | |
| 502 } | |
| 503 } | |
| 504 subPage.tab = undefined; | |
| 505 subPage.initializePage(); | |
| 506 }; | |
| 507 | |
| 508 /** | |
| 509 * Registers a new Overlay page. | 386 * Registers a new Overlay page. |
| 510 * @param {OptionsPage} overlay Overlay to register. | 387 * @param {OptionsPage} overlay Overlay to register. |
| 511 * @param {OptionsPage} parentPage Associated parent page for this overlay. | 388 * @param {OptionsPage} parentPage Associated parent page for this overlay. |
| 512 * @param {Array} associatedControls Array of control elements associated with | 389 * @param {Array} associatedControls Array of control elements associated with |
| 513 * this page. | 390 * this page. |
| 514 */ | 391 */ |
| 515 OptionsPage.registerOverlay = function(overlay, | 392 OptionsPage.registerOverlay = function(overlay, |
| 516 parentPage, | 393 parentPage, |
| 517 associatedControls) { | 394 associatedControls) { |
| 518 this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay; | 395 this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 577 * Callback for window.onbeforeunload. Used to notify overlays that they will | 454 * Callback for window.onbeforeunload. Used to notify overlays that they will |
| 578 * be closed. | 455 * be closed. |
| 579 */ | 456 */ |
| 580 OptionsPage.willClose = function() { | 457 OptionsPage.willClose = function() { |
| 581 var overlay = this.getVisibleOverlay_(); | 458 var overlay = this.getVisibleOverlay_(); |
| 582 if (overlay && overlay.didClosePage) | 459 if (overlay && overlay.didClosePage) |
| 583 overlay.didClosePage(); | 460 overlay.didClosePage(); |
| 584 }; | 461 }; |
| 585 | 462 |
| 586 /** | 463 /** |
| 587 * Freezes/unfreezes the scroll position of given level's page container. | 464 * Freezes/unfreezes the scroll position of the root page container. |
| 588 * @param {boolean} freeze Whether the page should be frozen. | 465 * @param {boolean} freeze Whether the page should be frozen. |
| 589 * @param {number} level The level to freeze/unfreeze. | |
| 590 * @private | 466 * @private |
| 591 */ | 467 */ |
| 592 OptionsPage.setPageFrozenAtLevel_ = function(freeze, level) { | 468 OptionsPage.setRootPageFrozen_ = function(freeze) { |
| 593 var container = level == 0 ? $('page-container') : | 469 var container = $('page-container'); |
| 594 $('subpage-sheet-container-' + level); | |
| 595 | |
| 596 if (container.classList.contains('frozen') == freeze) | 470 if (container.classList.contains('frozen') == freeze) |
| 597 return; | 471 return; |
| 598 | 472 |
| 599 if (freeze) { | 473 if (freeze) { |
| 600 // Lock the width, since auto width computation may change. | 474 // Lock the width, since auto width computation may change. |
| 601 container.style.width = window.getComputedStyle(container).width; | 475 container.style.width = window.getComputedStyle(container).width; |
| 602 container.oldScrollTop = document.body.scrollTop; | 476 container.oldScrollTop = document.body.scrollTop; |
| 603 container.classList.add('frozen'); | 477 container.classList.add('frozen'); |
| 604 var verticalPosition = | 478 var verticalPosition = |
| 605 container.getBoundingClientRect().top - container.oldScrollTop; | 479 container.getBoundingClientRect().top - container.oldScrollTop; |
| 606 container.style.top = verticalPosition + 'px'; | 480 container.style.top = verticalPosition + 'px'; |
| 607 this.updateFrozenElementHorizontalPosition_(container); | 481 this.updateFrozenElementHorizontalPosition_(container); |
| 608 } else { | 482 } else { |
| 609 container.classList.remove('frozen'); | 483 container.classList.remove('frozen'); |
| 610 container.style.top = ''; | 484 container.style.top = ''; |
| 611 container.style.left = ''; | 485 container.style.left = ''; |
| 612 container.style.right = ''; | 486 container.style.right = ''; |
| 613 container.style.width = ''; | 487 container.style.width = ''; |
| 614 } | 488 } |
| 615 }; | 489 }; |
| 616 | 490 |
| 617 /** | 491 /** |
| 618 * Freezes/unfreezes the scroll position of visible pages based on the current | 492 * Freezes/unfreezes the scroll position of the root page based on the current |
| 619 * page stack. | 493 * page stack. |
| 620 */ | 494 */ |
| 621 OptionsPage.updatePageFreezeStates = function() { | 495 OptionsPage.updateRootPageFreezeState = function() { |
| 622 var topPage = OptionsPage.getTopmostVisiblePage(); | 496 var topPage = OptionsPage.getTopmostVisiblePage(); |
| 623 if (!topPage) | 497 if (topPage) |
| 624 return; | 498 this.setRootPageFrozen_(topPage.isOverlay); |
| 625 var nestingLevel = topPage.isOverlay ? 100 : topPage.nestingLevel; | |
| 626 for (var i = 0; i <= SUBPAGE_SHEET_COUNT; i++) { | |
| 627 this.setPageFrozenAtLevel_(i < nestingLevel, i); | |
| 628 } | |
| 629 }; | 499 }; |
| 630 | 500 |
| 631 /** | 501 /** |
| 632 * Initializes the complete options page. This will cause all C++ handlers to | 502 * Initializes the complete options page. This will cause all C++ handlers to |
| 633 * be invoked to do final setup. | 503 * be invoked to do final setup. |
| 634 */ | 504 */ |
| 635 OptionsPage.initialize = function() { | 505 OptionsPage.initialize = function() { |
| 636 chrome.send('coreOptionsInitialize'); | 506 chrome.send('coreOptionsInitialize'); |
| 637 this.initialized_ = true; | 507 this.initialized_ = true; |
| 638 uber.onContentFrameLoaded(); | 508 uber.onContentFrameLoaded(); |
| 639 | 509 |
| 640 this.fixedHeaders_ = document.querySelectorAll('header'); | 510 this.fixedHeaders_ = document.querySelectorAll('header'); |
| 641 | 511 |
| 642 document.addEventListener('scroll', this.handleScroll_.bind(this)); | 512 document.addEventListener('scroll', this.handleScroll_.bind(this)); |
| 643 window.addEventListener('resize', this.handleResize_.bind(this)); | |
| 644 | 513 |
| 645 if (!document.documentElement.classList.contains('hide-menu')) { | 514 // Trigger the scroll handler manually to set the initial state. |
| 646 // Close subpages if the user clicks on the html body. Listen in the | |
| 647 // capturing phase so that we can stop the click from doing anything. | |
| 648 document.body.addEventListener('click', | |
| 649 this.bodyMouseEventHandler_.bind(this), | |
| 650 true); | |
| 651 // We also need to cancel mousedowns on non-subpage content. | |
| 652 document.body.addEventListener('mousedown', | |
| 653 this.bodyMouseEventHandler_.bind(this), | |
| 654 true); | |
| 655 | |
| 656 var self = this; | |
| 657 // Hook up the close buttons. | |
| 658 subpageCloseButtons = document.querySelectorAll('.close-subpage'); | |
| 659 for (var i = 0; i < subpageCloseButtons.length; i++) { | |
| 660 subpageCloseButtons[i].onclick = function() { | |
| 661 self.closeTopSubPage_(); | |
| 662 }; | |
| 663 } | |
| 664 | |
| 665 // Install handler for key presses. | |
| 666 document.addEventListener('keydown', | |
| 667 this.keyDownEventHandler_.bind(this), | |
| 668 true); | |
| 669 | |
| 670 document.addEventListener('focus', this.manageFocusChange_.bind(this), | |
| 671 true); | |
| 672 } | |
| 673 | |
| 674 // Trigger the resize and scroll handlers manually to set the initial state. | |
| 675 this.handleResize_(null); | |
| 676 this.handleScroll_(); | 515 this.handleScroll_(); |
| 677 | 516 |
| 678 // Shake the dialog if the user clicks outside the dialog bounds. | 517 // Shake the dialog if the user clicks outside the dialog bounds. |
| 679 var containers = [$('overlay-container-1'), $('overlay-container-2')]; | 518 var containers = [$('overlay-container-1'), $('overlay-container-2')]; |
| 680 for (var i = 0; i < containers.length; i++) { | 519 for (var i = 0; i < containers.length; i++) { |
| 681 var overlay = containers[i]; | 520 var overlay = containers[i]; |
| 682 cr.ui.overlay.setupOverlay(overlay); | 521 cr.ui.overlay.setupOverlay(overlay); |
| 683 overlay.addEventListener('closeOverlay', | 522 overlay.addEventListener('closeOverlay', |
| 684 OptionsPage.closeOverlay.bind(OptionsPage)); | 523 OptionsPage.closeOverlay.bind(OptionsPage)); |
| 685 } | 524 } |
| 686 }; | 525 }; |
| 687 | 526 |
| 688 /** | 527 /** |
| 689 * Does a bounds check for the element on the given x, y client coordinates. | 528 * Does a bounds check for the element on the given x, y client coordinates. |
| 690 * @param {Element} e The DOM element. | 529 * @param {Element} e The DOM element. |
| 691 * @param {number} x The client X to check. | 530 * @param {number} x The client X to check. |
| 692 * @param {number} y The client Y to check. | 531 * @param {number} y The client Y to check. |
| 693 * @return {boolean} True if the point falls within the element's bounds. | 532 * @return {boolean} True if the point falls within the element's bounds. |
| 694 * @private | 533 * @private |
| 695 */ | 534 */ |
| 696 OptionsPage.elementContainsPoint_ = function(e, x, y) { | 535 OptionsPage.elementContainsPoint_ = function(e, x, y) { |
| 697 var clientRect = e.getBoundingClientRect(); | 536 var clientRect = e.getBoundingClientRect(); |
| 698 return x >= clientRect.left && x <= clientRect.right && | 537 return x >= clientRect.left && x <= clientRect.right && |
| 699 y >= clientRect.top && y <= clientRect.bottom; | 538 y >= clientRect.top && y <= clientRect.bottom; |
| 700 }; | 539 }; |
| 701 | 540 |
| 702 /** | 541 /** |
| 703 * Called when focus changes; ensures that focus doesn't move outside | |
| 704 * the topmost subpage/overlay. | |
| 705 * @param {Event} e The focus change event. | |
| 706 * @private | |
| 707 */ | |
| 708 OptionsPage.manageFocusChange_ = function(e) { | |
| 709 var focusableItemsRoot; | |
| 710 var topPage = this.getTopmostVisiblePage(); | |
| 711 if (!topPage) | |
| 712 return; | |
| 713 | |
| 714 if (topPage.isOverlay) { | |
| 715 // This case is handled in overlay.js. | |
| 716 return; | |
| 717 } else { | |
| 718 // If a subpage is visible, use its parent as the tab loop constraint. | |
| 719 // (The parent is used because it contains the close button.) | |
| 720 if (topPage.nestingLevel > 0) | |
| 721 focusableItemsRoot = topPage.pageDiv.parentNode; | |
| 722 } | |
| 723 | |
| 724 if (focusableItemsRoot && !focusableItemsRoot.contains(e.target)) | |
| 725 topPage.focusFirstElement(); | |
| 726 }; | |
| 727 | |
| 728 /** | |
| 729 * Called when the page is scrolled; moves elements that are position:fixed | 542 * Called when the page is scrolled; moves elements that are position:fixed |
| 730 * but should only behave as if they are fixed for vertical scrolling. | 543 * but should only behave as if they are fixed for vertical scrolling. |
| 731 * @private | 544 * @private |
| 732 */ | 545 */ |
| 733 OptionsPage.handleScroll_ = function() { | 546 OptionsPage.handleScroll_ = function() { |
| 734 this.updateAllFrozenElementPositions_(); | 547 this.updateAllFrozenElementPositions_(); |
| 735 this.updateAllHeaderElementPositions_(); | 548 this.updateAllHeaderElementPositions_(); |
| 736 }; | 549 }; |
| 737 | 550 |
| 738 /** | 551 /** |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 763 * @param {HTMLElement} e The frozen element to update. | 576 * @param {HTMLElement} e The frozen element to update. |
| 764 * @private | 577 * @private |
| 765 */ | 578 */ |
| 766 OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) { | 579 OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) { |
| 767 if (isRTL()) | 580 if (isRTL()) |
| 768 e.style.right = HORIZONTAL_OFFSET + 'px'; | 581 e.style.right = HORIZONTAL_OFFSET + 'px'; |
| 769 else | 582 else |
| 770 e.style.left = HORIZONTAL_OFFSET - document.body.scrollLeft + 'px'; | 583 e.style.left = HORIZONTAL_OFFSET - document.body.scrollLeft + 'px'; |
| 771 }; | 584 }; |
| 772 | 585 |
| 773 /** | |
| 774 * Called when the page is resized; adjusts the size of elements that depend | |
| 775 * on the veiwport. | |
| 776 * @param {Event} e The resize event. | |
| 777 * @private | |
| 778 */ | |
| 779 OptionsPage.handleResize_ = function(e) { | |
| 780 // Set an explicit height equal to the viewport on all the subpage | |
| 781 // containers shorter than the viewport. This is used instead of | |
| 782 // min-height: 100% so that there is an explicit height for the subpages' | |
| 783 // min-height: 100%. | |
| 784 var viewportHeight = document.documentElement.clientHeight; | |
| 785 var subpageContainers = | |
| 786 document.querySelectorAll('.subpage-sheet-container'); | |
| 787 for (var i = 0; i < subpageContainers.length; i++) { | |
| 788 if (subpageContainers[i].scrollHeight > viewportHeight) | |
| 789 subpageContainers[i].style.removeProperty('height'); | |
| 790 else | |
| 791 subpageContainers[i].style.height = viewportHeight + 'px'; | |
| 792 } | |
| 793 }; | |
| 794 | |
| 795 /** | |
| 796 * A function to handle mouse events (mousedown or click) on the html body by | |
| 797 * closing subpages and/or stopping event propagation. | |
| 798 * @return {Event} a mousedown or click event. | |
| 799 * @private | |
| 800 */ | |
| 801 OptionsPage.bodyMouseEventHandler_ = function(event) { | |
| 802 // Do nothing if a subpage isn't showing. | |
| 803 var topPage = this.getTopmostVisiblePage(); | |
| 804 if (!topPage || topPage.isOverlay || !topPage.parentPage) | |
| 805 return; | |
| 806 | |
| 807 // Don't close subpages if a user is clicking in a select element. | |
| 808 // This is necessary because WebKit sends click events with strange | |
| 809 // coordinates when a user selects a new entry in a select element. | |
| 810 // See: http://crbug.com/87199 | |
| 811 if (event.srcElement.nodeName == 'SELECT') | |
| 812 return; | |
| 813 | |
| 814 // Do nothing if the client coordinates are not within the source element. | |
| 815 // This occurs if the user toggles a checkbox by pressing spacebar. | |
| 816 // This is a workaround to prevent keyboard events from closing the window. | |
| 817 // See: crosbug.com/15678 | |
| 818 if (event.clientX == -document.body.scrollLeft && | |
| 819 event.clientY == -document.body.scrollTop) { | |
| 820 return; | |
| 821 } | |
| 822 | |
| 823 // Figure out which page the click happened in. | |
| 824 for (var level = topPage.nestingLevel; level >= 0; level--) { | |
| 825 var clickIsWithinLevel = level == 0 ? true : | |
| 826 OptionsPage.elementContainsPoint_( | |
| 827 $('subpage-sheet-' + level), event.clientX, event.clientY); | |
| 828 | |
| 829 if (!clickIsWithinLevel) | |
| 830 continue; | |
| 831 | |
| 832 // Event was within the topmost page; do nothing. | |
| 833 if (topPage.nestingLevel == level) | |
| 834 return; | |
| 835 | |
| 836 // Block propgation of both clicks and mousedowns, but only close subpages | |
| 837 // on click. | |
| 838 if (event.type == 'click') | |
| 839 this.closeSubPagesToLevel(level); | |
| 840 event.stopPropagation(); | |
| 841 event.preventDefault(); | |
| 842 return; | |
| 843 } | |
| 844 }; | |
| 845 | |
| 846 /** | |
| 847 * A function to handle key press events. | |
| 848 * @return {Event} a keydown event. | |
| 849 * @private | |
| 850 */ | |
| 851 OptionsPage.keyDownEventHandler_ = function(event) { | |
| 852 if (event.keyCode == 27 && // Esc | |
| 853 !this.isOverlayVisible_()) { | |
| 854 this.closeTopSubPage_(); | |
| 855 } | |
| 856 }; | |
| 857 | |
| 858 OptionsPage.setClearPluginLSODataEnabled = function(enabled) { | 586 OptionsPage.setClearPluginLSODataEnabled = function(enabled) { |
| 859 if (enabled) { | 587 if (enabled) { |
| 860 document.documentElement.setAttribute( | 588 document.documentElement.setAttribute( |
| 861 'flashPluginSupportsClearSiteData', ''); | 589 'flashPluginSupportsClearSiteData', ''); |
| 862 } else { | 590 } else { |
| 863 document.documentElement.removeAttribute( | 591 document.documentElement.removeAttribute( |
| 864 'flashPluginSupportsClearSiteData'); | 592 'flashPluginSupportsClearSiteData'); |
| 865 } | 593 } |
| 866 }; | 594 }; |
| 867 | 595 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 966 /** | 694 /** |
| 967 * Sets page visibility. | 695 * Sets page visibility. |
| 968 */ | 696 */ |
| 969 set visible(visible) { | 697 set visible(visible) { |
| 970 if ((this.visible && visible) || (!this.visible && !visible)) | 698 if ((this.visible && visible) || (!this.visible && !visible)) |
| 971 return; | 699 return; |
| 972 | 700 |
| 973 this.pageDiv.hidden = !visible; | 701 this.pageDiv.hidden = !visible; |
| 974 this.setContainerVisibility_(visible); | 702 this.setContainerVisibility_(visible); |
| 975 | 703 |
| 976 OptionsPage.updatePageFreezeStates(); | 704 OptionsPage.updateRootPageFreezeState(); |
| 977 OptionsPage.updateManagedBannerVisibility(); | 705 OptionsPage.updateManagedBannerVisibility(); |
| 978 | 706 |
| 979 // A subpage was shown or hidden. | 707 if (this.isOverlay && !visible) |
| 980 if (!this.isOverlay && this.nestingLevel > 0) | |
| 981 OptionsPage.updateDisplayForShowOrHideSubpage_(); | |
| 982 else if (this.isOverlay && !visible) | |
| 983 OptionsPage.updateScrollPosition_(); | 708 OptionsPage.updateScrollPosition_(); |
| 984 | 709 |
| 985 cr.dispatchPropertyChange(this, 'visible', visible, !visible); | 710 cr.dispatchPropertyChange(this, 'visible', visible, !visible); |
| 986 }, | 711 }, |
| 987 | 712 |
| 988 /** | 713 /** |
| 989 * Shows or hides this page's container. | 714 * Shows or hides this page's container. |
| 990 * @param {boolean} visible Whether the container should be visible or not. | 715 * @param {boolean} visible Whether the container should be visible or not. |
| 991 * @private | 716 * @private |
| 992 */ | 717 */ |
| 993 setContainerVisibility_: function(visible) { | 718 setContainerVisibility_: function(visible) { |
| 994 var container = null; | 719 var container = this.isOverlay ? this.pageDiv.parentNode : null; |
|
Dan Beam
2012/03/06 00:31:41
this looks a little strange to me, but I guess it'
csilv
2012/03/06 01:03:04
Done.
| |
| 995 if (this.isOverlay) { | |
| 996 container = this.pageDiv.parentNode; | |
| 997 } else { | |
| 998 var nestingLevel = this.nestingLevel; | |
| 999 if (nestingLevel > 0) | |
| 1000 container = $('subpage-sheet-container-' + nestingLevel); | |
| 1001 } | |
| 1002 var isSubpage = !this.isOverlay; | |
| 1003 | |
| 1004 if (!container) | 720 if (!container) |
| 1005 return; | 721 return; |
| 1006 | 722 |
| 1007 if (visible) | 723 if (visible) |
| 1008 uber.invokeMethodOnParent('beginInterceptingEvents'); | 724 uber.invokeMethodOnParent('beginInterceptingEvents'); |
| 1009 | 725 |
| 1010 if (container.hidden != visible) { | 726 if (container.hidden != visible) { |
| 1011 if (visible) { | 727 if (visible) { |
| 1012 // If the container is set hidden and then immediately set visible | 728 // If the container is set hidden and then immediately set visible |
| 1013 // again, the fadeCompleted_ callback would cause it to be erroneously | 729 // again, the fadeCompleted_ callback would cause it to be erroneously |
| 1014 // hidden again. Removing the transparent tag avoids that. | 730 // hidden again. Removing the transparent tag avoids that. |
| 1015 container.classList.remove('transparent'); | 731 container.classList.remove('transparent'); |
| 1016 } | 732 } |
| 1017 return; | 733 return; |
| 1018 } | 734 } |
| 1019 | 735 |
| 1020 if (visible) { | 736 if (visible) { |
| 1021 container.hidden = false; | 737 container.hidden = false; |
| 1022 if (document.documentElement.classList.contains('loading')) { | 738 if (document.documentElement.classList.contains('loading')) { |
| 1023 container.classList.remove('transparent'); | 739 container.classList.remove('transparent'); |
| 1024 } else { | 740 } else { |
| 1025 if (isSubpage) { | |
| 1026 var computedStyle = window.getComputedStyle(container); | |
| 1027 container.style.WebkitPaddingStart = | |
| 1028 parseInt(computedStyle.WebkitPaddingStart, 10) + 100 + 'px'; | |
| 1029 } | |
| 1030 // Separate animating changes from the removal of display:none. | 741 // Separate animating changes from the removal of display:none. |
| 1031 window.setTimeout(function() { | 742 window.setTimeout(function() { |
| 1032 container.classList.remove('transparent'); | 743 container.classList.remove('transparent'); |
| 1033 if (isSubpage) | |
| 1034 container.style.WebkitPaddingStart = ''; | |
| 1035 }); | 744 }); |
| 1036 } | 745 } |
| 1037 } else { | 746 } else { |
| 1038 var self = this; | 747 var self = this; |
| 1039 container.addEventListener('webkitTransitionEnd', function f(e) { | 748 container.addEventListener('webkitTransitionEnd', function f(e) { |
| 1040 if (e.propertyName != 'opacity') | 749 if (e.propertyName != 'opacity') |
| 1041 return; | 750 return; |
| 1042 container.removeEventListener('webkitTransitionEnd', f); | 751 container.removeEventListener('webkitTransitionEnd', f); |
| 1043 self.fadeCompleted_(container); | 752 self.fadeCompleted_(container); |
| 1044 }); | 753 }); |
| 1045 container.classList.add('transparent'); | 754 container.classList.add('transparent'); |
| 1046 } | 755 } |
| 1047 }, | 756 }, |
| 1048 | 757 |
| 1049 /** | 758 /** |
| 1050 * Called when a container opacity transition finishes. | 759 * Called when a container opacity transition finishes. |
| 1051 * @param {HTMLElement} container The container element. | 760 * @param {HTMLElement} container The container element. |
| 1052 * @private | 761 * @private |
| 1053 */ | 762 */ |
| 1054 fadeCompleted_: function(container) { | 763 fadeCompleted_: function(container) { |
| 1055 if (container.classList.contains('transparent')) { | 764 if (container.classList.contains('transparent')) { |
| 1056 container.hidden = true; | 765 container.hidden = true; |
| 1057 if (this.nestingLevel == 1) | 766 if (this.nestingLevel == 1) |
| 1058 uber.invokeMethodOnParent('stopInterceptingEvents'); | 767 uber.invokeMethodOnParent('stopInterceptingEvents'); |
| 1059 } | 768 } |
| 1060 }, | 769 }, |
| 1061 | 770 |
| 1062 /** | 771 /** |
| 1063 * Focuses the first control on the page. | |
| 1064 */ | |
| 1065 focusFirstElement: function() { | |
| 1066 // Sets focus on the first interactive element in the page. | |
| 1067 var focusElement = | |
| 1068 this.pageDiv.querySelector('button, input, list, select'); | |
| 1069 if (focusElement) | |
| 1070 focusElement.focus(); | |
| 1071 }, | |
| 1072 | |
| 1073 /** | |
| 1074 * The nesting level of this page. | 772 * The nesting level of this page. |
| 1075 * @type {number} The nesting level of this page (0 for top-level page) | 773 * @type {number} The nesting level of this page (0 for top-level page) |
| 1076 */ | 774 */ |
| 1077 get nestingLevel() { | 775 get nestingLevel() { |
| 1078 var level = 0; | 776 var level = 0; |
| 1079 var parent = this.parentPage; | 777 var parent = this.parentPage; |
| 1080 while (parent) { | 778 while (parent) { |
| 1081 level++; | 779 level++; |
| 1082 parent = parent.parentPage; | 780 parent = parent.parentPage; |
| 1083 } | 781 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1116 canShowPage: function() { | 814 canShowPage: function() { |
| 1117 return true; | 815 return true; |
| 1118 }, | 816 }, |
| 1119 }; | 817 }; |
| 1120 | 818 |
| 1121 // Export | 819 // Export |
| 1122 return { | 820 return { |
| 1123 OptionsPage: OptionsPage | 821 OptionsPage: OptionsPage |
| 1124 }; | 822 }; |
| 1125 }); | 823 }); |
| OLD | NEW |