diff --git a/core/misc/tabbingmanager.js b/core/misc/tabbingmanager.js index 3848c7c..392daa5 100644 --- a/core/misc/tabbingmanager.js +++ b/core/misc/tabbingmanager.js @@ -7,20 +7,10 @@ "use strict"; -Drupal.behaviors.drupalTabbingManager = { - attach: function (context, settings) { - // Do not process the window of the overlay. - if (parent.Drupal.overlay && parent.Drupal.overlay.iframeWindow === window) { - return; - } - // Mark this behavior as processed on the first pass and return if it is - // already processed. - if (this.tabbingManagerProcessed) { - return; - } - this.tabbingManagerProcessed = true; - } -}; +if (parent.Drupal.overlay && parent.Drupal.overlay.iframeWindow === window) { + return; +} + /** * Provides an API for managing page tabbing order modifications. @@ -50,7 +40,7 @@ $.extend(TabbingManager.prototype, { * * @return TabbingContext */ - constrain: function (set) { + constrain: function (elements) { // Deactivate all tabbingContexts to prepare for the new constraint. A // tabbingContext instance will only be reactivated if the stack is unwound // to it in the _unwindStack() method. @@ -60,16 +50,13 @@ $.extend(TabbingManager.prototype, { // The "active tabbing set" are the elements tabbing should be constrained // to. - var $set = $(set).find(':tabbable').addBack(':tabbable'); + var $elements = $(elements).find(':tabbable').addBack(':tabbable'); var tabbingContext = new TabbingContext({ // The level is the current height of the stack before this new // tabbingContext is pushed on top of the stack. level: this.stack.length, - $tabbableElements: $set, - onRelease: $.proxy(this._unwindStack, this), - onActivate: $.proxy(this._activateTabbingContext, this), - onDeactivate: $.proxy(this._deactivateTabbingContext, this) + $tabbableElements: $elements }); this.stack.push(tabbingContext); @@ -91,20 +78,20 @@ $.extend(TabbingManager.prototype, { * the top-most released tabbingContext down to the first non-released * tabbingContext instance. This non-released instance is then activated. */ - _unwindStack: function () { + release: function () { // Unwind as far as possible: find the topmost non-released tabbingContext. - var tabbingContextToActivate = this.stack.length - 1; - while (tabbingContextToActivate >= 0 && this.stack[tabbingContextToActivate].isReleased()) { - tabbingContextToActivate--; + var toActivate = this.stack.length - 1; + while (toActivate >= 0 && this.stack[toActivate].released) { + toActivate--; } // Delete all tabbingContexts after the to be activated one. They have // already been deactivated, so their effect on the DOM has been reversed. - this.stack.splice(tabbingContextToActivate + 1); + this.stack.splice(toActivate + 1); // Get topmost tabbingContext, if one exists, and activate it. - if (tabbingContextToActivate >= 0) { - this.stack[tabbingContextToActivate].activate(); + if (toActivate >= 0) { + this.stack[toActivate].activate(); } }, @@ -118,17 +105,19 @@ $.extend(TabbingManager.prototype, { * @param TabbingContext tabbingContext * The TabbingContext instance that has been activated. */ - _activateTabbingContext: function (tabbingContext) { - var $set = tabbingContext.getTabbableElements(); - var level = tabbingContext.getLevel(); + activate: function (tabbingContext) { + var $set = tabbingContext.$tabbableElements; + var level = tabbingContext.level; // Determine which elements are reachable via tabbing by default. var $disabledSet = $(':tabbable') // Exclude elements of the active tabbing set. .not($set); // Set the disabled set on the tabbingContext. - tabbingContext.setDisabledElements($disabledSet); + tabbingContext.$disabledElements = $disabledSet; // Record the tabindex for each element, so we can restore it later. - this._recordTabindex($disabledSet, level); + for (var i = 0, il = $set.length; i < il; i++) { + this.recordTabindex($set.eq(i), level); + } // Make all tabbable elements outside of the active tabbing set unreachable. $disabledSet .prop('tabindex', -1) @@ -155,10 +144,12 @@ $.extend(TabbingManager.prototype, { * @param TabbingContext tabbingContext * The TabbingContext instance that has been deactivated. */ - _deactivateTabbingContext: function (tabbingContext) { - var $set = tabbingContext.getDisabledElements(); - var level = tabbingContext.getLevel(); - this._restoreTabindex($set, level); + deactivate: function (tabbingContext) { + var $set = tabbingContext.$disabledElements; + var level = tabbingContext.level; + for (var i = 0, il = $set.length; i < il; i++) { + this.restoreTabindex($set.eq(i), level); + } }, /** @@ -169,17 +160,13 @@ $.extend(TabbingManager.prototype, { * @param Number level * The stack level for which the tabindex attribute should be recorded. */ - _recordTabindex: function ($set, level) { - for (var i = 0, il = $set.length; i < il; i++) { - var $el = $set.eq(i); - var el = $set[i]; - var tabInfo = $el.data('drupalOriginalTabIndices') || {}; - tabInfo[level] = { - tabindex: el.getAttribute('tabindex'), - autofocus: el.hasAttribute('autofocus') - }; - $el.data('drupalOriginalTabIndices', tabInfo); - } + recordTabindex: function ($el, level) { + var tabInfo = $el.data('drupalOriginalTabIndices') || {}; + tabInfo[level] = { + tabindex: $el[0].getAttribute('tabindex'), + autofocus: $el[0].hasAttribute('autofocus') + }; + $el.data('drupalOriginalTabIndices', tabInfo); }, /** @@ -190,39 +177,35 @@ $.extend(TabbingManager.prototype, { * @param Number level * The stack level for which the tabindex attribute should be restored. */ - _restoreTabindex: function ($set, level) { - for (var i = 0, il = $set.length; i < il; i++) { - var $el = $set.eq(i); - var el = $set[i]; - var tabInfo = $el.data('drupalOriginalTabIndices') || {}; - if (tabInfo && tabInfo[level]) { - var data = tabInfo[level]; - if (data.tabindex) { - el.setAttribute('tabindex', data.tabindex); - } - // If the element did not have a tabindex at this stack level then - // remove it. - else { - el.removeAttribute('tabindex'); - } - if (data.autofocus) { - el.setAttribute('autofocus', 'autofocus'); - } + restoreTabindex: function ($el, level) { + var tabInfo = $el.data('drupalOriginalTabIndices'); + if (tabInfo && tabInfo[level]) { + var data = tabInfo[level]; + if (data.tabindex) { + $el[0].setAttribute('tabindex', data.tabindex); + } + // If the element did not have a tabindex at this stack level then + // remove it. + else { + $el[0].removeAttribute('tabindex'); + } + if (data.autofocus) { + $el[0].setAttribute('autofocus', 'autofocus'); + } - // Clean up $.data. - if (level === 0) { - // Remove all data. - $el.removeData('drupalOriginalTabIndices'); - } - else { - // Remove the data for this stack level and higher. - var stackLevelToDelete = level; - while (tabInfo.hasOwnProperty(stackLevelToDelete)) { - delete tabInfo[stackLevelToDelete]; - stackLevelToDelete++; - } - $el.data('drupalOriginalTabIndices', tabInfo); + // Clean up $.data. + if (level === 0) { + // Remove all data. + $el.removeData('drupalOriginalTabIndices'); + } + else { + // Remove the data for this stack level and higher. + var levelToDelete = level; + while (tabInfo.hasOwnProperty(levelToDelete)) { + delete tabInfo[levelToDelete]; + levelToDelete++; } + $el.data('drupalOriginalTabIndices', tabInfo); } } } @@ -259,10 +242,7 @@ function TabbingContext (options) { $tabbableElements: $(), $disabledElements: $(), released: false, - active: false, - onRelease: function () {}, - onActivate: function () {}, - onDeactivate: function () {} + active: false }, options); } @@ -270,46 +250,6 @@ function TabbingContext (options) { * Add public methods to the TabbingContext class. */ $.extend(TabbingContext.prototype, { - - /** - * Returns the level of this tabbingContext in the TabbingManager stack. - * - * @return Number - */ - getLevel: function () { - return this.level; - }, - - /** - * Returns the elements that would have their tabbing disabled when this - * tabbingContext instance is active. - * - * @return jQuery - */ - getDisabledElements: function () { - return this.$disabledElements; - }, - - /** - * Stores the jQuery set of elements that will have their tabbing disabled. - * - * @param jQuery $set - * The jQuery set of elements that will have their tabbing disabled. - */ - setDisabledElements: function ($set) { - this.$disabledElements = $set; - }, - - /** - * Returns the elements that would be tabbable when this tabbingContext - * instance is active. - * - * @return jQuery - */ - getTabbableElements: function () { - return this.$tabbableElements; - }, - /** * Sets the released flag to true and calls the onRelease callback. * @@ -318,37 +258,20 @@ $.extend(TabbingContext.prototype, { release: function () { if (!this.released) { this.released = true; - // Releasing a tabbingContext means it is permanently deactivated. - this.deactivate(); - var args = Array.prototype.slice.call(arguments); - args.unshift(this); - this.onRelease.apply(null, args); - + Drupal.tabbingManager.release(this); // Allow modules to respond to the tabbingContext release event. $(document).trigger('drupalTabbingContextReleased', this); } }, /** - * Returns the state of the released flag. - * - * @return Boolean - */ - isReleased: function () { - return this.released; - }, - - /** * Sets the active flag to true and calls the onActivate callback. */ activate: function () { // A released TabbingContext object can never be activated again. if (!this.active && !this.released) { this.active = true; - var args = Array.prototype.slice.call(arguments); - args.unshift(this); - this.onActivate.apply(null, args); - + Drupal.tabbingManager.activate(this); // Allow modules to respond to the constrain event. $(document).trigger('drupalTabbingContextActivated', this); } @@ -360,30 +283,20 @@ $.extend(TabbingContext.prototype, { deactivate: function () { if (this.active) { this.active = false; - var args = Array.prototype.slice.call(arguments); - args.unshift(this); - this.onDeactivate.apply(null, args); - + Drupal.tabbingManager.deactivate(this); // Allow modules to respond to the constrain event. $(document).trigger('drupalTabbingContextDeactivated', this); } - }, - - /** - * Returns the state of the active flag. - * - * Only one TabbingContext can be active at a time. The disabled elements of - * an active TabbingContext will be unreachable via the tab key. - * - * @return Boolean - */ - isActive: function () { - return this.active; } }); -// Create a TabbingManager instance and assign it to the Drupal namespace. -var manager = new TabbingManager(); -Drupal.TabbingManager = manager; + +// Mark this behavior as processed on the first pass and return if it is +// already processed. +if (Drupal.tabbingManager) { + return; +} +Drupal.tabbingManager = new TabbingManager(); + }(jQuery, Drupal)); diff --git a/core/modules/contextual/contextual.toolbar.js b/core/modules/contextual/contextual.toolbar.js index c5032e6..c50da18 100644 --- a/core/modules/contextual/contextual.toolbar.js +++ b/core/modules/contextual/contextual.toolbar.js @@ -149,7 +149,7 @@ Drupal.contextualToolbar.views.EditToggleView = Backbone.View.extend({ } // Create a new tabbing context when edit mode is enabled. if (!isViewing) { - tabbingContext = Drupal.TabbingManager.constrain($('.contextual-toolbar-tab, .contextual')); + tabbingContext = Drupal.tabbingManager.constrain($('.contextual-toolbar-tab, .contextual')); this.model.set('tabbingContext', tabbingContext); } }, diff --git a/core/modules/overlay/overlay-parent.js b/core/modules/overlay/overlay-parent.js index f94b6e4..3356580 100644 --- a/core/modules/overlay/overlay-parent.js +++ b/core/modules/overlay/overlay-parent.js @@ -838,11 +838,11 @@ Drupal.overlay.getPath = function (link) { */ Drupal.overlay.constrainTabbing = function ($tabbables) { // If a tabset is already active, return without creating a new one. - if (this.tabset && !this.tabset.isReleased()) { + if (this.tabset && !this.tabset.released) { return; } // Leave links inside the overlay and toolbars alone. - this.tabset = Drupal.TabbingManager.constrain($tabbables); + this.tabset = Drupal.tabbingManager.constrain($tabbables); var self = this; $(document).on('drupalOverlayClose.tabbing', function () { self.tabset.release();