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

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

Issue 6315012: DOMUI Prefs: Keep focus within the topmost page/overlay (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments Created 9 years, 11 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 | « no previous file | 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) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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.
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 this.registeredOverlayPages[overlayName].visible = true; 122 this.registeredOverlayPages[overlayName].visible = true;
123 } 123 }
124 }; 124 };
125 125
126 /** 126 /**
127 * Returns whether or not an overlay is visible. 127 * Returns whether or not an overlay is visible.
128 * @return {boolean} True if an overlay is visible. 128 * @return {boolean} True if an overlay is visible.
129 * @private 129 * @private
130 */ 130 */
131 OptionsPage.isOverlayVisible_ = function() { 131 OptionsPage.isOverlayVisible_ = function() {
132 for (var name in this.registeredOverlayPages) { 132 return this.getVisibleOverlay_() != null;
133 if (this.registeredOverlayPages[name].visible)
134 return true;
135 }
136 return false;
137 }; 133 };
138 134
139 /** 135 /**
136 * Returns the currently visible overlay, or null if no page is visible.
137 * @return {OptionPage} The visible overlay.
138 */
139 OptionsPage.getVisibleOverlay_ = function() {
140 for (var name in this.registeredOverlayPages) {
141 var page = this.registeredOverlayPages[name];
142 if (page.visible)
143 return page;
144 }
145 return null;
146 };
147
148 /**
140 * Clears overlays (i.e. hide all overlays). 149 * Clears overlays (i.e. hide all overlays).
141 */ 150 */
142 OptionsPage.clearOverlays = function() { 151 OptionsPage.clearOverlays = function() {
143 for (var name in this.registeredOverlayPages) { 152 for (var name in this.registeredOverlayPages) {
144 var page = this.registeredOverlayPages[name]; 153 var page = this.registeredOverlayPages[name];
145 page.visible = false; 154 page.visible = false;
146 } 155 }
147 }; 156 };
148 157
149 /** 158 /**
150 * Returns the topmost visible page, or null if no page is visible. 159 * Returns the topmost visible page, or null if no page is visible.
151 * @return {OptionPage} The topmost visible page. 160 * @return {OptionPage} The topmost visible page.
152 */ 161 */
153 OptionsPage.getTopmostVisiblePage = function() { 162 OptionsPage.getTopmostVisiblePage = function() {
154 var topPage = null; 163 var topPage = null;
155 for (var name in this.registeredPages) { 164 for (var name in this.registeredPages) {
156 var page = this.registeredPages[name]; 165 var page = this.registeredPages[name];
157 if (page.visible && 166 if (page.visible &&
158 (!topPage || page.nestingLevel > topPage.nestingLevel)) 167 (!topPage || page.nestingLevel > topPage.nestingLevel))
159 topPage = page; 168 topPage = page;
160 } 169 }
161 return topPage; 170 return topPage;
162 } 171 };
163 172
164 /** 173 /**
165 * Closes the topmost open subpage, if any. 174 * Closes the topmost open subpage, if any.
166 */ 175 */
167 OptionsPage.closeTopSubPage = function() { 176 OptionsPage.closeTopSubPage = function() {
168 var topPage = this.getTopmostVisiblePage(); 177 var topPage = this.getTopmostVisiblePage();
169 if (topPage && topPage.parentPage) 178 if (topPage && topPage.parentPage)
170 topPage.visible = false; 179 topPage.visible = false;
171 }; 180 };
172 181
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 // Hook up the close buttons. 351 // Hook up the close buttons.
343 subpageCloseButtons = document.querySelectorAll('.close-subpage'); 352 subpageCloseButtons = document.querySelectorAll('.close-subpage');
344 for (var i = 0; i < subpageCloseButtons.length; i++) { 353 for (var i = 0; i < subpageCloseButtons.length; i++) {
345 subpageCloseButtons[i].onclick = function() { 354 subpageCloseButtons[i].onclick = function() {
346 self.closeTopSubPage(); 355 self.closeTopSubPage();
347 }; 356 };
348 }; 357 };
349 358
350 // Install handler for key presses. 359 // Install handler for key presses.
351 document.addEventListener('keydown', this.keyDownEventHandler_.bind(this)); 360 document.addEventListener('keydown', this.keyDownEventHandler_.bind(this));
361
362 document.addEventListener('focus', this.manageFocusChange_.bind(this),
363 true);
352 }; 364 };
353 365
354 /** 366 /**
355 * Returns a function to handle clicks behind a subpage at level |level| by 367 * Returns a function to handle clicks behind a subpage at level |level| by
356 * closing all subpages down to |level| - 1. 368 * closing all subpages down to |level| - 1.
357 * @param {number} level The level of the subpage being handled. 369 * @param {number} level The level of the subpage being handled.
358 * @return {Function} a function to handle clicks outside the given subpage. 370 * @return {Function} a function to handle clicks outside the given subpage.
359 * @private 371 * @private
360 */ 372 */
361 OptionsPage.subPageClosingClickHandler_ = function(level) { 373 OptionsPage.subPageClosingClickHandler_ = function(level) {
362 var self = this; 374 var self = this;
363 return function(event) { 375 return function(event) {
364 // Clicks on the narrow strip between the left of the subpage sheet and 376 // Clicks on the narrow strip between the left of the subpage sheet and
365 // that shows part of the parent page should close the overlay, but 377 // that shows part of the parent page should close the overlay, but
366 // not fall through to the parent page. 378 // not fall through to the parent page.
367 if (!$('subpage-sheet-' + level).contains(event.target)) { 379 if (!$('subpage-sheet-' + level).contains(event.target)) {
368 self.closeSubPagesToLevel(level - 1); 380 self.closeSubPagesToLevel(level - 1);
369 event.stopPropagation(); 381 event.stopPropagation();
370 event.preventDefault(); 382 event.preventDefault();
371 } 383 }
372 }; 384 };
373 }; 385 };
374 386
375 /** 387 /**
388 * Called when focus changes; ensures that focus doesn't move outside
389 * the topmost subpage/overlay.
390 * @param {Event} e The focus change event.
391 * @private
392 */
393 OptionsPage.manageFocusChange_ = function(e) {
394 var focusableItemsRoot = null;
arv (Not doing code reviews) 2011/01/26 17:55:49 no need to assign null here.
stuartmorgan 2011/01/26 18:19:08 Done.
395 // If an overlay is visible, that defines the tab loop.
396 var topPage = this.getVisibleOverlay_();
397 if (topPage)
398 focusableItemsRoot = topPage.pageDiv;
399 // If a subpage is visible, use its parent as the tab loop constraint.
400 // (The parent is used because it contains the close button.)
401 if (!topPage) {
402 var topPage = this.getTopmostVisiblePage();
403 if (topPage && topPage.nestingLevel > 0)
404 focusableItemsRoot = topPage.pageDiv.parentNode;
405 }
406
407 if (focusableItemsRoot && !focusableItemsRoot.contains(e.target))
408 topPage.focusFirstElement();
409 };
410
411 /**
376 * A function to handle mouse events (mousedown or click) on the html body by 412 * A function to handle mouse events (mousedown or click) on the html body by
377 * closing subpages and/or stopping event propagation. 413 * closing subpages and/or stopping event propagation.
378 * @return {Event} a mousedown or click event. 414 * @return {Event} a mousedown or click event.
379 * @private 415 * @private
380 */ 416 */
381 OptionsPage.bodyMouseEventHandler_ = function(event) { 417 OptionsPage.bodyMouseEventHandler_ = function(event) {
382 // Do nothing if a subpage isn't showing. 418 // Do nothing if a subpage isn't showing.
383 var topPage = this.getTopmostVisiblePage(); 419 var topPage = this.getTopmostVisiblePage();
384 if (!(topPage && topPage.parentPage)) 420 if (!(topPage && topPage.parentPage))
385 return; 421 return;
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 } 571 }
536 if (this.tab) { 572 if (this.tab) {
537 this.tab.classList.remove('navbar-item-selected'); 573 this.tab.classList.remove('navbar-item-selected');
538 } 574 }
539 } 575 }
540 576
541 cr.dispatchPropertyChange(this, 'visible', visible, !visible); 577 cr.dispatchPropertyChange(this, 'visible', visible, !visible);
542 }, 578 },
543 579
544 /** 580 /**
581 * Focuses the first control on the page.
582 */
583 focusFirstElement: function() {
584 // Sets focus on the first interactive element in the page.
585 focusElement = this.pageDiv.querySelector('button, input, list, select');
arv (Not doing code reviews) 2011/01/26 17:55:49 missing var
stuartmorgan 2011/01/26 18:19:08 Done.
586 if (focusElement)
587 focusElement.focus();
588 },
589
590 /**
545 * The nesting level of this page. 591 * The nesting level of this page.
546 * @type {number} The nesting level of this page (0 for top-level page) 592 * @type {number} The nesting level of this page (0 for top-level page)
547 */ 593 */
548 get nestingLevel() { 594 get nestingLevel() {
549 var level = 0; 595 var level = 0;
550 var parent = this.parentPage; 596 var parent = this.parentPage;
551 while (parent) { 597 while (parent) {
552 level++; 598 level++;
553 parent = parent.parentPage; 599 parent = parent.parentPage;
554 } 600 }
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
591 OptionsPage.showOverlay(hash); 637 OptionsPage.showOverlay(hash);
592 }, 638 },
593 }; 639 };
594 640
595 // Export 641 // Export
596 return { 642 return {
597 OptionsPage: OptionsPage 643 OptionsPage: OptionsPage
598 }; 644 };
599 645
600 }); 646 });
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698