Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(266)

Side by Side Diff: chrome/browser/resources/options2/options_page.js

Issue 9554007: [uber page] Remove obsolete subpage logic. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: code review tweaks Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/resources/options2/options_page.css ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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. This handles both top-level and overlay pages.
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
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() {
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
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
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
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
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
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 if (!this.isOverlay)
995 if (this.isOverlay) { 720 return;
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 721
1004 if (!container) 722 var container = this.pageDiv.parentNode;
1005 return;
1006 723
1007 if (visible) 724 if (visible)
1008 uber.invokeMethodOnParent('beginInterceptingEvents'); 725 uber.invokeMethodOnParent('beginInterceptingEvents');
1009 726
1010 if (container.hidden != visible) { 727 if (container.hidden != visible) {
1011 if (visible) { 728 if (visible) {
1012 // If the container is set hidden and then immediately set visible 729 // If the container is set hidden and then immediately set visible
1013 // again, the fadeCompleted_ callback would cause it to be erroneously 730 // again, the fadeCompleted_ callback would cause it to be erroneously
1014 // hidden again. Removing the transparent tag avoids that. 731 // hidden again. Removing the transparent tag avoids that.
1015 container.classList.remove('transparent'); 732 container.classList.remove('transparent');
1016 } 733 }
1017 return; 734 return;
1018 } 735 }
1019 736
1020 if (visible) { 737 if (visible) {
1021 container.hidden = false; 738 container.hidden = false;
1022 if (document.documentElement.classList.contains('loading')) { 739 if (document.documentElement.classList.contains('loading')) {
1023 container.classList.remove('transparent'); 740 container.classList.remove('transparent');
1024 } else { 741 } 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. 742 // Separate animating changes from the removal of display:none.
1031 window.setTimeout(function() { 743 window.setTimeout(function() {
1032 container.classList.remove('transparent'); 744 container.classList.remove('transparent');
1033 if (isSubpage)
1034 container.style.WebkitPaddingStart = '';
1035 }); 745 });
1036 } 746 }
1037 } else { 747 } else {
1038 var self = this; 748 var self = this;
1039 container.addEventListener('webkitTransitionEnd', function f(e) { 749 container.addEventListener('webkitTransitionEnd', function f(e) {
1040 if (e.propertyName != 'opacity') 750 if (e.propertyName != 'opacity')
1041 return; 751 return;
1042 container.removeEventListener('webkitTransitionEnd', f); 752 container.removeEventListener('webkitTransitionEnd', f);
1043 self.fadeCompleted_(container); 753 self.fadeCompleted_(container);
1044 }); 754 });
1045 container.classList.add('transparent'); 755 container.classList.add('transparent');
1046 } 756 }
1047 }, 757 },
1048 758
1049 /** 759 /**
1050 * Called when a container opacity transition finishes. 760 * Called when a container opacity transition finishes.
1051 * @param {HTMLElement} container The container element. 761 * @param {HTMLElement} container The container element.
1052 * @private 762 * @private
1053 */ 763 */
1054 fadeCompleted_: function(container) { 764 fadeCompleted_: function(container) {
1055 if (container.classList.contains('transparent')) { 765 if (container.classList.contains('transparent')) {
1056 container.hidden = true; 766 container.hidden = true;
1057 if (this.nestingLevel == 1) 767 if (this.nestingLevel == 1)
1058 uber.invokeMethodOnParent('stopInterceptingEvents'); 768 uber.invokeMethodOnParent('stopInterceptingEvents');
1059 } 769 }
1060 }, 770 },
1061 771
1062 /** 772 /**
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. 773 * The nesting level of this page.
1075 * @type {number} The nesting level of this page (0 for top-level page) 774 * @type {number} The nesting level of this page (0 for top-level page)
1076 */ 775 */
1077 get nestingLevel() { 776 get nestingLevel() {
1078 var level = 0; 777 var level = 0;
1079 var parent = this.parentPage; 778 var parent = this.parentPage;
1080 while (parent) { 779 while (parent) {
1081 level++; 780 level++;
1082 parent = parent.parentPage; 781 parent = parent.parentPage;
1083 } 782 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1116 canShowPage: function() { 815 canShowPage: function() {
1117 return true; 816 return true;
1118 }, 817 },
1119 }; 818 };
1120 819
1121 // Export 820 // Export
1122 return { 821 return {
1123 OptionsPage: OptionsPage 822 OptionsPage: OptionsPage
1124 }; 823 };
1125 }); 824 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/options2/options_page.css ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698