From 1b03f5fc987c1641c706adbc83fddc220c323079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?"J.=20Rene=CC=81e=20Beach"?= Date: Sat, 9 Feb 2013 17:36:07 -0500 Subject: [PATCH] Issue #1913086 by jessebeach: Generalize the overlay tabbing management into a utility library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 71f1d5b266f4a5fe8ac4dcf978b97f952c62b41c Author: J. Renée Beach Date: Sat Feb 9 17:25:23 2013 -0500 retabbing Signed-off-by: J. Renée Beach Signed-off-by: J. Renée Beach --- core/misc/tabbingmanager.js | 101 ++++++++++++++++++++++++++++++++ core/modules/overlay/overlay-parent.js | 84 +------------------------- core/modules/overlay/overlay.module | 1 + core/modules/system/system.module | 9 +++ 4 files changed, 114 insertions(+), 81 deletions(-) create mode 100644 core/misc/tabbingmanager.js diff --git a/core/misc/tabbingmanager.js b/core/misc/tabbingmanager.js new file mode 100644 index 0000000..9c8724b --- /dev/null +++ b/core/misc/tabbingmanager.js @@ -0,0 +1,101 @@ +(function ($, Drupal) { + +"use strict"; + +function TabbingManager () { + + return { + constrain: function () {}, + remove: function () {}, + makeDocumentUntabbable: makeDocumentUntabbable, + makeDocumentTabbable: makeDocumentTabbable + }; +} + +/** + * Makes elements outside the overlay unreachable via the tab key. + * + * @param context + * The part of the DOM that should have its tabindexes changed. Defaults to + * the entire page. + */ +var makeDocumentUntabbable = function (context) { + context = context || document.body; + var $overlay, $tabbable, $hasTabindex; + + // Determine which elements on the page already have a tabindex. + $hasTabindex = $('[tabindex] :not(.overlay-element)', context); + // Record the tabindex for each element, so we can restore it later. + $hasTabindex.recordTabindex(); + // Add the tabbable elements from the current context to any that we might + // have previously recorded. + Drupal.overlay._hasTabindex = $hasTabindex.add(Drupal.overlay._hasTabindex); + + // Set tabindex to -1 on everything outside the overlay and toolbars, so that + // the underlying page is unreachable. + + // By default, browsers make a, area, button, input, object, select, textarea, + // and iframe elements reachable via the tab key. + $tabbable = $('a, area, button, input, object, select, textarea, iframe'); + // If another element (like a div) has a tabindex, it's also tabbable. + $tabbable = $tabbable.add($hasTabindex); + // Leave links inside the overlay and toolbars alone. + $overlay = $('.overlay-element, #overlay-container, .overlay-displace-top, .overlay-displace-bottom').find('*'); + $tabbable = $tabbable.not($overlay); + // We now have a list of everything in the underlying document that could + // possibly be reachable via the tab key. Make it all unreachable. + $tabbable.attr('tabindex', -1); +}; + +/** + * Restores the original tabindex value of a group of elements. + * + * @param context + * The part of the DOM that should have its tabindexes restored. Defaults to + * the entire page. + */ +var makeDocumentTabbable = function (context) { + var $needsTabindex; + context = context || document.body; + + // Make the underlying document tabbable again by removing all existing + // tabindex attributes. + $('[tabindex]', context).removeAttr('tabindex'); + + // Restore the tabindex attributes that existed before the overlay was opened. + $needsTabindex = $(Drupal.overlay._hasTabindex, context); + $needsTabindex.restoreTabindex(); + Drupal.overlay._hasTabindex = Drupal.overlay._hasTabindex.not($needsTabindex); +}; + +/** + * Record the tabindex for an element, using $.data. + * + * Meant to be used as a jQuery.fn.each callback. + */ +$.fn.recordTabindex = function () { + return this.each(function () { + var $element = $(this); + var tabindex = $(this).attr('tabindex'); + $element.data('drupalOverlayOriginalTabIndex', tabindex); + }); +}; + +/** + * Restore an element's original tabindex. + * + * Meant to be used as a jQuery.fn.each callback. + */ +$.fn.restoreTabindex = function () { + return this.each(function () { + var $element = $(this); + var tabindex = $element.data('drupalOverlayOriginalTabIndex'); + $element.attr('tabindex', tabindex); + }); +}; + +var manager = new TabbingManager(); + +Drupal.TabbingManager = manager; + +}(jQuery, Drupal)); diff --git a/core/modules/overlay/overlay-parent.js b/core/modules/overlay/overlay-parent.js index caf9336..49a9fc3 100644 --- a/core/modules/overlay/overlay-parent.js +++ b/core/modules/overlay/overlay-parent.js @@ -13,7 +13,7 @@ Drupal.behaviors.overlayParent = { attach: function (context, settings) { if (Drupal.overlay.isOpen) { - Drupal.overlay.makeDocumentUntabbable(context); + Drupal.TabbingManager.makeDocumentUntabbable(context); } if (this.processed) { @@ -94,7 +94,7 @@ Drupal.overlay.open = function (url) { this.isOpening = false; this.isOpen = true; $(document.documentElement).addClass('overlay-open'); - this.makeDocumentUntabbable(); + Drupal.TabbingManager.makeDocumentUntabbable(); // Allow other scripts to respond to this event. $(document).trigger('drupalOverlayOpen'); @@ -201,7 +201,7 @@ Drupal.overlay.close = function () { $(document.documentElement).removeClass('overlay-open'); // Restore the original document title. document.title = this.originalTitle; - this.makeDocumentTabbable(); + Drupal.TabbingManager.makeDocumentTabbable(); // Allow other scripts to respond to this event. $(document).trigger('drupalOverlayClose'); @@ -888,84 +888,6 @@ Drupal.overlay.getDisplacement = function (region) { return displacement; }; -/** - * Makes elements outside the overlay unreachable via the tab key. - * - * @param context - * The part of the DOM that should have its tabindexes changed. Defaults to - * the entire page. - */ -Drupal.overlay.makeDocumentUntabbable = function (context) { - context = context || document.body; - var $overlay, $tabbable, $hasTabindex; - - // Determine which elements on the page already have a tabindex. - $hasTabindex = $('[tabindex] :not(.overlay-element)', context); - // Record the tabindex for each element, so we can restore it later. - $hasTabindex.each(Drupal.overlay._recordTabindex); - // Add the tabbable elements from the current context to any that we might - // have previously recorded. - Drupal.overlay._hasTabindex = $hasTabindex.add(Drupal.overlay._hasTabindex); - - // Set tabindex to -1 on everything outside the overlay and toolbars, so that - // the underlying page is unreachable. - - // By default, browsers make a, area, button, input, object, select, textarea, - // and iframe elements reachable via the tab key. - $tabbable = $('a, area, button, input, object, select, textarea, iframe'); - // If another element (like a div) has a tabindex, it's also tabbable. - $tabbable = $tabbable.add($hasTabindex); - // Leave links inside the overlay and toolbars alone. - $overlay = $('.overlay-element, #overlay-container, .overlay-displace-top, .overlay-displace-bottom').find('*'); - $tabbable = $tabbable.not($overlay); - // We now have a list of everything in the underlying document that could - // possibly be reachable via the tab key. Make it all unreachable. - $tabbable.attr('tabindex', -1); -}; - -/** - * Restores the original tabindex value of a group of elements. - * - * @param context - * The part of the DOM that should have its tabindexes restored. Defaults to - * the entire page. - */ -Drupal.overlay.makeDocumentTabbable = function (context) { - var $needsTabindex; - context = context || document.body; - - // Make the underlying document tabbable again by removing all existing - // tabindex attributes. - $('[tabindex]', context).removeAttr('tabindex'); - - // Restore the tabindex attributes that existed before the overlay was opened. - $needsTabindex = $(Drupal.overlay._hasTabindex, context); - $needsTabindex.each(Drupal.overlay._restoreTabindex); - Drupal.overlay._hasTabindex = Drupal.overlay._hasTabindex.not($needsTabindex); -}; - -/** - * Record the tabindex for an element, using $.data. - * - * Meant to be used as a jQuery.fn.each callback. - */ -Drupal.overlay._recordTabindex = function () { - var $element = $(this); - var tabindex = $(this).attr('tabindex'); - $element.data('drupalOverlayOriginalTabIndex', tabindex); -}; - -/** - * Restore an element's original tabindex. - * - * Meant to be used as a jQuery.fn.each callback. - */ -Drupal.overlay._restoreTabindex = function () { - var $element = $(this); - var tabindex = $element.data('drupalOverlayOriginalTabIndex'); - $element.attr('tabindex', tabindex); -}; - $.extend(Drupal.theme, { /** * Theme function to create the overlay iframe element. diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module index 1893a15..5f83241 100644 --- a/core/modules/overlay/overlay.module +++ b/core/modules/overlay/overlay.module @@ -210,6 +210,7 @@ function overlay_library_info() { $module_path . '/overlay-parent.css' => array(), ), 'dependencies' => array( + array('system', 'drupal.tabbingmanager'), array('system', 'jquery'), array('system', 'drupal'), array('system', 'drupalSettings'), diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 223af2e..b8927b0 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1383,6 +1383,15 @@ function system_library_info() { ), ); + // Manages tab orders in the document. + $libraries['drupal.tabbingmanager'] = array( + 'title' => 'Drupal tabbing manager', + 'version' => VERSION, + 'js' => array( + 'core/misc/tabbingmanager.js' => array('group', JS_LIBRARY), + ), + ); + // A utility function to limit calls to a function with a given time. $libraries['drupal.debounce'] = array( 'title' => 'Drupal debounce', -- 1.7.10.4