| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 | 
							- /**
 
-  * @file
 
-  * Manages page tabbing modifications made by modules.
 
-  */
 
- /**
 
-  * Allow modules to respond to the constrain event.
 
-  *
 
-  * @event drupalTabbingConstrained
 
-  */
 
- /**
 
-  * Allow modules to respond to the tabbingContext release event.
 
-  *
 
-  * @event drupalTabbingContextReleased
 
-  */
 
- /**
 
-  * Allow modules to respond to the constrain event.
 
-  *
 
-  * @event drupalTabbingContextActivated
 
-  */
 
- /**
 
-  * Allow modules to respond to the constrain event.
 
-  *
 
-  * @event drupalTabbingContextDeactivated
 
-  */
 
- (function ($, Drupal) {
 
-   /**
 
-    * Provides an API for managing page tabbing order modifications.
 
-    *
 
-    * @constructor Drupal~TabbingManager
 
-    */
 
-   function TabbingManager() {
 
-     /**
 
-      * Tabbing sets are stored as a stack. The active set is at the top of the
 
-      * stack. We use a JavaScript array as if it were a stack; we consider the
 
-      * first element to be the bottom and the last element to be the top. This
 
-      * allows us to use JavaScript's built-in Array.push() and Array.pop()
 
-      * methods.
 
-      *
 
-      * @type {Array.<Drupal~TabbingContext>}
 
-      */
 
-     this.stack = [];
 
-   }
 
-   /**
 
-    * Add public methods to the TabbingManager class.
 
-    */
 
-   $.extend(TabbingManager.prototype, /** @lends Drupal~TabbingManager# */{
 
-     /**
 
-      * Constrain tabbing to the specified set of elements only.
 
-      *
 
-      * Makes elements outside of the specified set of elements unreachable via
 
-      * the tab key.
 
-      *
 
-      * @param {jQuery} elements
 
-      *   The set of elements to which tabbing should be constrained. Can also
 
-      *   be a jQuery-compatible selector string.
 
-      *
 
-      * @return {Drupal~TabbingContext}
 
-      *   The TabbingContext instance.
 
-      *
 
-      * @fires event:drupalTabbingConstrained
 
-      */
 
-     constrain(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.
 
-       const il = this.stack.length;
 
-       for (let i = 0; i < il; i++) {
 
-         this.stack[i].deactivate();
 
-       }
 
-       // The "active tabbing set" are the elements tabbing should be constrained
 
-       // to.
 
-       const $elements = $(elements).find(':tabbable').addBack(':tabbable');
 
-       const 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: $elements,
 
-       });
 
-       this.stack.push(tabbingContext);
 
-       // Activates the tabbingContext; this will manipulate the DOM to constrain
 
-       // tabbing.
 
-       tabbingContext.activate();
 
-       // Allow modules to respond to the constrain event.
 
-       $(document).trigger('drupalTabbingConstrained', tabbingContext);
 
-       return tabbingContext;
 
-     },
 
-     /**
 
-      * Restores a former tabbingContext when an active one is released.
 
-      *
 
-      * The TabbingManager stack of tabbingContext instances will be unwound
 
-      * from the top-most released tabbingContext down to the first non-released
 
-      * tabbingContext instance. This non-released instance is then activated.
 
-      */
 
-     release() {
 
-       // Unwind as far as possible: find the topmost non-released
 
-       // tabbingContext.
 
-       let 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(toActivate + 1);
 
-       // Get topmost tabbingContext, if one exists, and activate it.
 
-       if (toActivate >= 0) {
 
-         this.stack[toActivate].activate();
 
-       }
 
-     },
 
-     /**
 
-      * Makes all elements outside of the tabbingContext's set untabbable.
 
-      *
 
-      * Elements made untabbable have their original tabindex and autofocus
 
-      * values stored so that they might be restored later when this
 
-      * tabbingContext is deactivated.
 
-      *
 
-      * @param {Drupal~TabbingContext} tabbingContext
 
-      *   The TabbingContext instance that has been activated.
 
-      */
 
-     activate(tabbingContext) {
 
-       const $set = tabbingContext.$tabbableElements;
 
-       const level = tabbingContext.level;
 
-       // Determine which elements are reachable via tabbing by default.
 
-       const $disabledSet = $(':tabbable')
 
-         // Exclude elements of the active tabbing set.
 
-         .not($set);
 
-       // Set the disabled set on the tabbingContext.
 
-       tabbingContext.$disabledElements = $disabledSet;
 
-       // Record the tabindex for each element, so we can restore it later.
 
-       const il = $disabledSet.length;
 
-       for (let i = 0; i < il; i++) {
 
-         this.recordTabindex($disabledSet.eq(i), level);
 
-       }
 
-       // Make all tabbable elements outside of the active tabbing set
 
-       // unreachable.
 
-       $disabledSet
 
-         .prop('tabindex', -1)
 
-         .prop('autofocus', false);
 
-       // Set focus on an element in the tabbingContext's set of tabbable
 
-       // elements. First, check if there is an element with an autofocus
 
-       // attribute. Select the last one from the DOM order.
 
-       let $hasFocus = $set.filter('[autofocus]').eq(-1);
 
-       // If no element in the tabbable set has an autofocus attribute, select
 
-       // the first element in the set.
 
-       if ($hasFocus.length === 0) {
 
-         $hasFocus = $set.eq(0);
 
-       }
 
-       $hasFocus.trigger('focus');
 
-     },
 
-     /**
 
-      * Restores that tabbable state of a tabbingContext's disabled elements.
 
-      *
 
-      * Elements that were made untabbable have their original tabindex and
 
-      * autofocus values restored.
 
-      *
 
-      * @param {Drupal~TabbingContext} tabbingContext
 
-      *   The TabbingContext instance that has been deactivated.
 
-      */
 
-     deactivate(tabbingContext) {
 
-       const $set = tabbingContext.$disabledElements;
 
-       const level = tabbingContext.level;
 
-       const il = $set.length;
 
-       for (let i = 0; i < il; i++) {
 
-         this.restoreTabindex($set.eq(i), level);
 
-       }
 
-     },
 
-     /**
 
-      * Records the tabindex and autofocus values of an untabbable element.
 
-      *
 
-      * @param {jQuery} $el
 
-      *   The set of elements that have been disabled.
 
-      * @param {number} level
 
-      *   The stack level for which the tabindex attribute should be recorded.
 
-      */
 
-     recordTabindex($el, level) {
 
-       const tabInfo = $el.data('drupalOriginalTabIndices') || {};
 
-       tabInfo[level] = {
 
-         tabindex: $el[0].getAttribute('tabindex'),
 
-         autofocus: $el[0].hasAttribute('autofocus'),
 
-       };
 
-       $el.data('drupalOriginalTabIndices', tabInfo);
 
-     },
 
-     /**
 
-      * Restores the tabindex and autofocus values of a reactivated element.
 
-      *
 
-      * @param {jQuery} $el
 
-      *   The element that is being reactivated.
 
-      * @param {number} level
 
-      *   The stack level for which the tabindex attribute should be restored.
 
-      */
 
-     restoreTabindex($el, level) {
 
-       const tabInfo = $el.data('drupalOriginalTabIndices');
 
-       if (tabInfo && tabInfo[level]) {
 
-         const 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.
 
-           let levelToDelete = level;
 
-           while (tabInfo.hasOwnProperty(levelToDelete)) {
 
-             delete tabInfo[levelToDelete];
 
-             levelToDelete++;
 
-           }
 
-           $el.data('drupalOriginalTabIndices', tabInfo);
 
-         }
 
-       }
 
-     },
 
-   });
 
-   /**
 
-    * Stores a set of tabbable elements.
 
-    *
 
-    * This constraint can be removed with the release() method.
 
-    *
 
-    * @constructor Drupal~TabbingContext
 
-    *
 
-    * @param {object} options
 
-    *   A set of initiating values
 
-    * @param {number} options.level
 
-    *   The level in the TabbingManager's stack of this tabbingContext.
 
-    * @param {jQuery} options.$tabbableElements
 
-    *   The DOM elements that should be reachable via the tab key when this
 
-    *   tabbingContext is active.
 
-    * @param {jQuery} options.$disabledElements
 
-    *   The DOM elements that should not be reachable via the tab key when this
 
-    *   tabbingContext is active.
 
-    * @param {bool} options.released
 
-    *   A released tabbingContext can never be activated again. It will be
 
-    *   cleaned up when the TabbingManager unwinds its stack.
 
-    * @param {bool} options.active
 
-    *   When true, the tabbable elements of this tabbingContext will be reachable
 
-    *   via the tab key and the disabled elements will not. Only one
 
-    *   tabbingContext can be active at a time.
 
-    */
 
-   function TabbingContext(options) {
 
-     $.extend(this, /** @lends Drupal~TabbingContext# */{
 
-       /**
 
-        * @type {?number}
 
-        */
 
-       level: null,
 
-       /**
 
-        * @type {jQuery}
 
-        */
 
-       $tabbableElements: $(),
 
-       /**
 
-        * @type {jQuery}
 
-        */
 
-       $disabledElements: $(),
 
-       /**
 
-        * @type {bool}
 
-        */
 
-       released: false,
 
-       /**
 
-        * @type {bool}
 
-        */
 
-       active: false,
 
-     }, options);
 
-   }
 
-   /**
 
-    * Add public methods to the TabbingContext class.
 
-    */
 
-   $.extend(TabbingContext.prototype, /** @lends Drupal~TabbingContext# */{
 
-     /**
 
-      * Releases this TabbingContext.
 
-      *
 
-      * Once a TabbingContext object is released, it can never be activated
 
-      * again.
 
-      *
 
-      * @fires event:drupalTabbingContextReleased
 
-      */
 
-     release() {
 
-       if (!this.released) {
 
-         this.deactivate();
 
-         this.released = true;
 
-         Drupal.tabbingManager.release(this);
 
-         // Allow modules to respond to the tabbingContext release event.
 
-         $(document).trigger('drupalTabbingContextReleased', this);
 
-       }
 
-     },
 
-     /**
 
-      * Activates this TabbingContext.
 
-      *
 
-      * @fires event:drupalTabbingContextActivated
 
-      */
 
-     activate() {
 
-       // A released TabbingContext object can never be activated again.
 
-       if (!this.active && !this.released) {
 
-         this.active = true;
 
-         Drupal.tabbingManager.activate(this);
 
-         // Allow modules to respond to the constrain event.
 
-         $(document).trigger('drupalTabbingContextActivated', this);
 
-       }
 
-     },
 
-     /**
 
-      * Deactivates this TabbingContext.
 
-      *
 
-      * @fires event:drupalTabbingContextDeactivated
 
-      */
 
-     deactivate() {
 
-       if (this.active) {
 
-         this.active = false;
 
-         Drupal.tabbingManager.deactivate(this);
 
-         // Allow modules to respond to the constrain event.
 
-         $(document).trigger('drupalTabbingContextDeactivated', this);
 
-       }
 
-     },
 
-   });
 
-   // Mark this behavior as processed on the first pass and return if it is
 
-   // already processed.
 
-   if (Drupal.tabbingManager) {
 
-     return;
 
-   }
 
-   /**
 
-    * @type {Drupal~TabbingManager}
 
-    */
 
-   Drupal.tabbingManager = new TabbingManager();
 
- }(jQuery, Drupal));
 
 
  |