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

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: 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),
James Hawkins 2011/01/26 01:29:41 nit: All params on one line or lined up in one col
stuartmorgan 2011/01/26 17:27:03 The style guide explicitly allows either form.
James Hawkins 2011/01/26 18:08:25 Interesting. I had been told otherwise by a reputa
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 tabLoopRoot = null;
James Hawkins 2011/01/26 01:29:41 This var name is a little confusing.
stuartmorgan 2011/01/26 17:27:03 Changed to focusableItemsRoot; hopefully that's a
395 // If an overlay is visible, that defines the tab loop
James Hawkins 2011/01/26 01:29:41 nit: Period at end of sentence.
stuartmorgan 2011/01/26 17:27:03 Done.
396 var topPage = this.getVisibleOverlay_();
397 if (topPage)
398 tabLoopRoot = 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 tabLoopRoot = topPage.pageDiv.parentNode;
405 }
406
407 if (tabLoopRoot && !tabLoopRoot.contains(e.target)) {
James Hawkins 2011/01/26 01:29:41 nit: No need for braces.
stuartmorgan 2011/01/26 17:27:03 Done.
408 topPage.focusFirstElement();
409 }
410 };
411
412 /**
376 * A function to handle mouse events (mousedown or click) on the html body by 413 * A function to handle mouse events (mousedown or click) on the html body by
377 * closing subpages and/or stopping event propagation. 414 * closing subpages and/or stopping event propagation.
378 * @return {Event} a mousedown or click event. 415 * @return {Event} a mousedown or click event.
379 * @private 416 * @private
380 */ 417 */
381 OptionsPage.bodyMouseEventHandler_ = function(event) { 418 OptionsPage.bodyMouseEventHandler_ = function(event) {
382 // Do nothing if a subpage isn't showing. 419 // Do nothing if a subpage isn't showing.
383 var topPage = this.getTopmostVisiblePage(); 420 var topPage = this.getTopmostVisiblePage();
384 if (!(topPage && topPage.parentPage)) 421 if (!(topPage && topPage.parentPage))
385 return; 422 return;
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 } 572 }
536 if (this.tab) { 573 if (this.tab) {
537 this.tab.classList.remove('navbar-item-selected'); 574 this.tab.classList.remove('navbar-item-selected');
538 } 575 }
539 } 576 }
540 577
541 cr.dispatchPropertyChange(this, 'visible', visible, !visible); 578 cr.dispatchPropertyChange(this, 'visible', visible, !visible);
542 }, 579 },
543 580
544 /** 581 /**
582 * Focuses the first control on the page.
583 */
584 focusFirstElement: function() {
585 // Sets focus on the first interactive element in the page.
586 focusElement = this.pageDiv.querySelector('button, input, list, select');
587 if (focusElement)
588 focusElement.focus();
589 },
590
591 /**
545 * The nesting level of this page. 592 * The nesting level of this page.
546 * @type {number} The nesting level of this page (0 for top-level page) 593 * @type {number} The nesting level of this page (0 for top-level page)
547 */ 594 */
548 get nestingLevel() { 595 get nestingLevel() {
549 var level = 0; 596 var level = 0;
550 var parent = this.parentPage; 597 var parent = this.parentPage;
551 while (parent) { 598 while (parent) {
552 level++; 599 level++;
553 parent = parent.parentPage; 600 parent = parent.parentPage;
554 } 601 }
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
591 OptionsPage.showOverlay(hash); 638 OptionsPage.showOverlay(hash);
592 }, 639 },
593 }; 640 };
594 641
595 // Export 642 // Export
596 return { 643 return {
597 OptionsPage: OptionsPage 644 OptionsPage: OptionsPage
598 }; 645 };
599 646
600 }); 647 });
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