| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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, also defines id of the div element | 12 * @param {string} name Options page name, also defines id of the div element |
| 13 * containing the options view and the name of options page navigation bar | 13 * containing the options view and the name of options page navigation bar |
| 14 * item as name+'PageNav'. | 14 * item as name+'PageNav'. |
| 15 * @param {string} title Options page title, used for navigation bar | 15 * @param {string} title Options page title, used for navigation bar |
| 16 * @extends {EventTarget} | 16 * @extends {EventTarget} |
| 17 */ | 17 */ |
| 18 function OptionsPage(name, title, pageDivName) { | 18 function OptionsPage(name, title, pageDivName) { |
| 19 this.name = name; | 19 this.name = name; |
| 20 this.title = title; | 20 this.title = title; |
| 21 this.pageDivName = pageDivName; | 21 this.pageDivName = pageDivName; |
| 22 this.pageDiv = $(this.pageDivName); | 22 this.pageDiv = $(this.pageDivName); |
| 23 this.tab = null; | 23 this.tab = null; |
| 24 this.managed = false; | 24 this.managed = false; |
| 25 } | 25 } |
| 26 | 26 |
| 27 const SUBPAGE_SHEET_COUNT = 2; |
| 28 |
| 27 /** | 29 /** |
| 28 * Main level option pages. | 30 * Main level option pages. |
| 29 * @protected | 31 * @protected |
| 30 */ | 32 */ |
| 31 OptionsPage.registeredPages = {}; | 33 OptionsPage.registeredPages = {}; |
| 32 | 34 |
| 33 /** | 35 /** |
| 34 * Pages which are meant to behave like modal dialogs. | 36 * Pages which are meant to behave like modal dialogs. |
| 35 * @protected | 37 * @protected |
| 36 */ | 38 */ |
| (...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 if (data && data.pageName) { | 434 if (data && data.pageName) { |
| 433 // It's possible an overlay may be the last top-level page shown. | 435 // It's possible an overlay may be the last top-level page shown. |
| 434 if (this.isOverlayVisible_()) | 436 if (this.isOverlayVisible_()) |
| 435 this.hideOverlay_(); | 437 this.hideOverlay_(); |
| 436 | 438 |
| 437 this.showPageByName(data.pageName, false); | 439 this.showPageByName(data.pageName, false); |
| 438 } | 440 } |
| 439 }; | 441 }; |
| 440 | 442 |
| 441 /** | 443 /** |
| 442 * Freezes/unfreezes the scroll position of the top-level page based on | 444 * Freezes/unfreezes the scroll position of given level's page container. |
| 443 * whether a subpage is showing. | 445 * @param {boolean} freeze Whether the page should be frozen. |
| 446 * @param {number} level The level to freeze/unfreeze. |
| 447 * @private |
| 444 */ | 448 */ |
| 445 OptionsPage.updateTopLevelPageFreezeState = function() { | 449 OptionsPage.setPageFrozenAtLevel_ = function(freeze, level) { |
| 446 var freeze = OptionsPage.getTopmostVisiblePage().nestingLevel > 0; | 450 var container = level == 0 ? $('toplevel-page-container') |
| 447 var topPageContainer = $('toplevel-page-container'); | 451 : $('subpage-sheet-container-' + level); |
| 448 if (topPageContainer.classList.contains('frozen') == freeze) | 452 |
| 453 if (container.classList.contains('frozen') == freeze) |
| 449 return; | 454 return; |
| 450 | 455 |
| 451 if (freeze) { | 456 if (freeze) { |
| 452 var scrollPosition = document.body.scrollTop; | 457 var scrollPosition = document.body.scrollTop; |
| 453 // Lock the width, since auto width computation will change. | 458 // Lock the width, since auto width computation may change. |
| 454 topPageContainer.style.width = | 459 container.style.width = window.getComputedStyle(container).width; |
| 455 window.getComputedStyle(topPageContainer).width; | 460 container.classList.add('frozen'); |
| 456 topPageContainer.classList.add('frozen'); | 461 container.style.top = -scrollPosition + 'px'; |
| 457 topPageContainer.style.top = -scrollPosition + 'px'; | 462 this.updateFrozenElementHorizontalPosition_(container); |
| 458 this.updateFrozenPageHorizontalPosition_(); | |
| 459 } else { | 463 } else { |
| 460 var scrollPosition = - parseInt(topPageContainer.style.top, 10); | 464 var scrollPosition = - parseInt(container.style.top, 10); |
| 461 topPageContainer.classList.remove('frozen'); | 465 container.classList.remove('frozen'); |
| 462 topPageContainer.style.top = ''; | 466 container.style.top = ''; |
| 463 topPageContainer.style.left = ''; | 467 container.style.left = ''; |
| 464 topPageContainer.style.right = ''; | 468 container.style.right = ''; |
| 465 topPageContainer.style.width = ''; | 469 container.style.width = ''; |
| 466 // Restore the scroll position. | 470 // Restore the scroll position. |
| 467 window.scroll(document.body.scrollLeft, scrollPosition); | 471 if (!container.classList.contains('hidden')) |
| 472 window.scroll(document.body.scrollLeft, scrollPosition); |
| 468 } | 473 } |
| 469 }; | 474 }; |
| 470 | 475 |
| 476 /** |
| 477 * Freezes/unfreezes the scroll position of visible pages based on the current |
| 478 * page stack. |
| 479 */ |
| 480 OptionsPage.updatePageFreezeStates = function() { |
| 481 var topPage = OptionsPage.getTopmostVisiblePage(); |
| 482 if (!topPage) |
| 483 return; |
| 484 var nestingLevel = topPage.isOverlay ? 100 : topPage.nestingLevel; |
| 485 for (var i = 0; i <= SUBPAGE_SHEET_COUNT; i++) { |
| 486 this.setPageFrozenAtLevel_(i < nestingLevel, i); |
| 487 } |
| 488 }; |
| 489 |
| 471 /** | 490 /** |
| 472 * Initializes the complete options page. This will cause all C++ handlers to | 491 * Initializes the complete options page. This will cause all C++ handlers to |
| 473 * be invoked to do final setup. | 492 * be invoked to do final setup. |
| 474 */ | 493 */ |
| 475 OptionsPage.initialize = function() { | 494 OptionsPage.initialize = function() { |
| 476 chrome.send('coreOptionsInitialize'); | 495 chrome.send('coreOptionsInitialize'); |
| 477 this.initialized_ = true; | 496 this.initialized_ = true; |
| 478 | 497 |
| 479 var self = this; | 498 var self = this; |
| 480 // Close subpages if the user clicks on the html body. Listen in the | 499 // Close subpages if the user clicks on the html body. Listen in the |
| (...skipping 15 matching lines...) Expand all Loading... |
| 496 }; | 515 }; |
| 497 | 516 |
| 498 // Install handler for key presses. | 517 // Install handler for key presses. |
| 499 document.addEventListener('keydown', this.keyDownEventHandler_.bind(this)); | 518 document.addEventListener('keydown', this.keyDownEventHandler_.bind(this)); |
| 500 | 519 |
| 501 document.addEventListener('focus', this.manageFocusChange_.bind(this), | 520 document.addEventListener('focus', this.manageFocusChange_.bind(this), |
| 502 true); | 521 true); |
| 503 | 522 |
| 504 document.addEventListener('scroll', this.handleScroll_.bind(this)); | 523 document.addEventListener('scroll', this.handleScroll_.bind(this)); |
| 505 window.addEventListener('resize', this.handleResize_.bind(this)); | 524 window.addEventListener('resize', this.handleResize_.bind(this)); |
| 525 |
| 526 // Calculate and store the horizontal locations of elements that may be |
| 527 // frozen later. |
| 528 var sidebarWidth = |
| 529 parseInt(window.getComputedStyle($('mainview')).webkitPaddingStart, 10); |
| 530 $('toplevel-page-container').horizontalOffset = sidebarWidth + |
| 531 parseInt(window.getComputedStyle( |
| 532 $('mainview-content')).webkitPaddingStart, 10); |
| 533 for (var level = 1; level <= SUBPAGE_SHEET_COUNT; level++) { |
| 534 var containerId = 'subpage-sheet-container-' + level; |
| 535 $(containerId).horizontalOffset = sidebarWidth; |
| 536 } |
| 537 $('subpage-backdrop').horizontalOffset = sidebarWidth; |
| 506 // Trigger the resize handler manually to set the initial state. | 538 // Trigger the resize handler manually to set the initial state. |
| 507 this.handleResize_(null); | 539 this.handleResize_(null); |
| 508 }; | 540 }; |
| 509 | 541 |
| 510 /** | 542 /** |
| 511 * Does a bounds check for the element on the given x, y client coordinates. | 543 * Does a bounds check for the element on the given x, y client coordinates. |
| 512 * @param {Element} e The DOM element. | 544 * @param {Element} e The DOM element. |
| 513 * @param {number} x The client X to check. | 545 * @param {number} x The client X to check. |
| 514 * @param {number} y The client Y to check. | 546 * @param {number} y The client Y to check. |
| 515 * @return {boolean} True if the point falls within the element's bounds. | 547 * @return {boolean} True if the point falls within the element's bounds. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 547 topPage.focusFirstElement(); | 579 topPage.focusFirstElement(); |
| 548 }; | 580 }; |
| 549 | 581 |
| 550 /** | 582 /** |
| 551 * Called when the page is scrolled; moves elements that are position:fixed | 583 * Called when the page is scrolled; moves elements that are position:fixed |
| 552 * but should only behave as if they are fixed for vertical scrolling. | 584 * but should only behave as if they are fixed for vertical scrolling. |
| 553 * @param {Event} e The scroll event. | 585 * @param {Event} e The scroll event. |
| 554 * @private | 586 * @private |
| 555 */ | 587 */ |
| 556 OptionsPage.handleScroll_ = function(e) { | 588 OptionsPage.handleScroll_ = function(e) { |
| 557 var horizontalOffset = document.body.scrollLeft; | 589 var scrollHorizontalOffset = document.body.scrollLeft; |
| 558 // position:fixed doesn't seem to work for horizontal scrolling in RTL mode, | 590 // position:fixed doesn't seem to work for horizontal scrolling in RTL mode, |
| 559 // so only adjust in LTR mode (where scroll values will be positive). | 591 // so only adjust in LTR mode (where scroll values will be positive). |
| 560 if (horizontalOffset >= 0) { | 592 if (scrollHorizontalOffset >= 0) { |
| 561 $('navbar-container').style.left = -document.body.scrollLeft + 'px'; | 593 $('navbar-container').style.left = -scrollHorizontalOffset + 'px'; |
| 562 this.updateFrozenPageHorizontalPosition_(); | 594 var subpageBackdrop = $('subpage-backdrop'); |
| 595 subpageBackdrop.style.left = subpageBackdrop.horizontalOffset - |
| 596 scrollHorizontalOffset + 'px'; |
| 597 this.updateAllFrozenElementPositions_(); |
| 563 } | 598 } |
| 564 }; | 599 }; |
| 565 | 600 |
| 566 /** | 601 /** |
| 567 * Updates any frozen pages to match the horizontal scroll position. | 602 * Updates all frozen pages to match the horizontal scroll position. |
| 568 * @param {Event} e The scroll event. | |
| 569 * @private | 603 * @private |
| 570 */ | 604 */ |
| 571 OptionsPage.updateFrozenPageHorizontalPosition_ = function() { | 605 OptionsPage.updateAllFrozenElementPositions_ = function() { |
| 572 var horizontalOffset = document.body.scrollLeft; | 606 var frozenElements = document.querySelectorAll('.frozen'); |
| 573 var toplevelPage = $('toplevel-page-container'); | 607 for (var i = 0; i < frozenElements.length; i++) { |
| 574 if (toplevelPage.classList.contains('frozen')) { | 608 this.updateFrozenElementHorizontalPosition_(frozenElements[i]); |
| 575 const WINDOW_LEFT_OFFSET = 291; // Sidebar width + padding | |
| 576 if (document.documentElement.dir == 'rtl') { | |
| 577 toplevelPage.style.right = WINDOW_LEFT_OFFSET + 'px'; | |
| 578 } else { | |
| 579 toplevelPage.style.left = | |
| 580 WINDOW_LEFT_OFFSET -document.body.scrollLeft + 'px'; | |
| 581 } | |
| 582 } | 609 } |
| 583 }; | 610 }; |
| 584 | 611 |
| 585 /** | 612 /** |
| 613 * Updates the given frozen element to match the horizontal scroll position. |
| 614 * @param {HTMLElement} e The frozen element to update |
| 615 * @private |
| 616 */ |
| 617 OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) { |
| 618 if (document.documentElement.dir == 'rtl') |
| 619 e.style.right = e.horizontalOffset + 'px'; |
| 620 else |
| 621 e.style.left = e.horizontalOffset - document.body.scrollLeft + 'px'; |
| 622 }; |
| 623 |
| 624 /** |
| 586 * Called when the page is resized; adjusts the size of elements that depend | 625 * Called when the page is resized; adjusts the size of elements that depend |
| 587 * on the veiwport. | 626 * on the veiwport. |
| 588 * @param {Event} e The resize event. | 627 * @param {Event} e The resize event. |
| 589 * @private | 628 * @private |
| 590 */ | 629 */ |
| 591 OptionsPage.handleResize_ = function(e) { | 630 OptionsPage.handleResize_ = function(e) { |
| 592 // Set an explicit height equal to the viewport on all the subpage | 631 // Set an explicit height equal to the viewport on all the subpage |
| 593 // containers shorter than the viewport. This is used instead of | 632 // containers shorter than the viewport. This is used instead of |
| 594 // min-height: 100% so that there is an explicit height for the subpages' | 633 // min-height: 100% so that there is an explicit height for the subpages' |
| 595 // min-height: 100%. | 634 // min-height: 100%. |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 781 } | 820 } |
| 782 } | 821 } |
| 783 | 822 |
| 784 if (this.tab) | 823 if (this.tab) |
| 785 this.tab.classList.remove('navbar-item-selected'); | 824 this.tab.classList.remove('navbar-item-selected'); |
| 786 } | 825 } |
| 787 | 826 |
| 788 // A subpage was shown or hidden. | 827 // A subpage was shown or hidden. |
| 789 if (!this.isOverlay && this.nestingLevel > 0) { | 828 if (!this.isOverlay && this.nestingLevel > 0) { |
| 790 OptionsPage.updateSubpageBackdrop_(); | 829 OptionsPage.updateSubpageBackdrop_(); |
| 791 OptionsPage.updateTopLevelPageFreezeState(); | |
| 792 if (visible) { | 830 if (visible) { |
| 793 // Scroll to the top of the newly-opened subpage. | 831 // Scroll to the top of the newly-opened subpage. |
| 794 window.scroll(document.body.scrollLeft, 0) | 832 window.scroll(document.body.scrollLeft, 0) |
| 795 } | 833 } |
| 796 } | 834 } |
| 797 | 835 |
| 836 OptionsPage.updatePageFreezeStates(); |
| 798 // The managed prefs banner is global, so after any visibility change | 837 // The managed prefs banner is global, so after any visibility change |
| 799 // update it based on the topmost page, not necessarily this page | 838 // update it based on the topmost page, not necessarily this page |
| 800 // (e.g., if an ancestor is made visible after a child). | 839 // (e.g., if an ancestor is made visible after a child). |
| 801 OptionsPage.updateManagedBannerVisibility(); | 840 OptionsPage.updateManagedBannerVisibility(); |
| 802 | 841 |
| 803 cr.dispatchPropertyChange(this, 'visible', visible, !visible); | 842 cr.dispatchPropertyChange(this, 'visible', visible, !visible); |
| 804 }, | 843 }, |
| 805 | 844 |
| 806 /** | 845 /** |
| 807 * Focuses the first control on the page. | 846 * Focuses the first control on the page. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 860 canShowPage: function() { | 899 canShowPage: function() { |
| 861 return true; | 900 return true; |
| 862 }, | 901 }, |
| 863 }; | 902 }; |
| 864 | 903 |
| 865 // Export | 904 // Export |
| 866 return { | 905 return { |
| 867 OptionsPage: OptionsPage | 906 OptionsPage: OptionsPage |
| 868 }; | 907 }; |
| 869 }); | 908 }); |
| OLD | NEW |