text: /**
   * This mixin implements focus management functionality in Widgets and Components
   */
  Ext.define('Ext.mixin.Focusable', {
      mixinId: 'focusable',
      
      $isFocusableEntity: true,
      
      // tabIndex config is now defined in Ext.Component
      
      /**
       * @property {Boolean} focusable
       * @readonly
       *
       * `true` for keyboard interactive Components or Widgets, `false` otherwise.
       * For Containers, this property reflects interactiveness of the
       * Container itself, not its children. See {@link #isFocusable}.
       *
       * **Note:** It is not enough to set this property to `true` to make
       * a component keyboard interactive. You also need to make sure that
       * the component's {@link #focusEl} is reachable via Tab key (tabbable).
       * See also {@link #tabIndex}.
       */
      focusable: false,
  
      /**
       * @property {Boolean} hasFocus `true` if this component's {@link #focusEl} is focused.
       * See also {@link #containsFocus}.
       *
       * @readonly
       */
      hasFocus: false,
      
      /**
       * @property {Boolean} containsFocus `true` if this currently focused element
       * is within this Component's or Container's hierarchy. This property is set separately
       * from {@link #hasFocus}, and can be `true` when `hasFocus` is `false`.
       *
       * Examples:
       *
       * + Text field with input element focused would be:
       *      focusable: true,
       *      hasFocus: true,
       *      containsFocus: true
       *
       * + Date field with drop-down picker currently focused would be:
       *      focusable: true,
       *      hasFocus: false,
       *      containsFocus: true
       *
       * + Form Panel with a child input field currently focused would be:
       *      focusable: false,
       *      hasFocus: false,
       *      containsFocus: true
       *
       * See also {@link #hasFocus}.
       *
       * @readonly
       */
      containsFocus: false,
      
      /**
       * @cfg {String} [focusCls='x-focused'] CSS class that will be added to focused
       * component's {@link #focusClsEl}, and removed when component blurs.
       */
      focusCls: Ext.baseCSSPrefix + 'focused',
      
      /**
       * @property {Ext.dom.Element} focusEl The element that will be focused
       * when {@link #method!focus} method is called on this component. Usually this is
       * the same element that receives focus via mouse clicks, taps, and pressing
       * Tab key.
       */
      focusEl: 'el',
      
      /**
       * @property {Ext.dom.Element} focusClsEl The element that will have the
       * {@link #focusCls} applied when component's {@link #focusEl} is focused.
       */
      
      /**
       * @event focus
       * Fires when this Component's {@link #focusEl} receives focus.
       * @param {Ext.Component/Ext.Widget} this
       * @param {Ext.event.Event} event The focus event.
       */
  
      /**
       * @event blur
       * Fires when this Component's {@link #focusEl} loses focus.
       * @param {Ext.Component} this
       * @param {Ext.event.Event} event The blur event.
       */
      
      /**
       * @event focusenter
       * Fires when focus enters this Component's hierarchy.
       * @param {Ext.Component} this
       * @param {Ext.event.Event} event The focusenter event.
       */
      
      /**
       * @event focusleave
       * Fires when focus leaves this Component's hierarchy.
       * @param {Ext.Component} this
       * @param {Ext.event.Event} event The focusleave event.
       */
      
      /**
       * Returns the main focus holder element associated with this Focusable, i.e.
       * the element that will be focused when Focusable's {@link #method!focus} method is
       * called. For most Focusables, this will be the {@link #focusEl}.
       *
       * @return {Ext.dom.Element}
       * @protected
       */
      getFocusEl: function(/* private e */) {
          var focusEl = this.focusEl;
          
          return focusEl && focusEl.dom ? focusEl : null;
      },
      
      /**
       * Returns the element used to apply focus styling CSS class when Focusable's
       * {@link #focusEl} becomes focused. By default it is {@link #focusEl}.
       *
       * @param {Ext.dom.Element} [focusEl] Return focus styling element for the given
       * focused element. This is used by Components implementing multiple focusable
       * elements.
       *
       * @return {Ext.dom.Element} The focus styling element.
       * @protected
       */
      getFocusClsEl: function() {
          return this.getFocusEl();
      },
      
      /**
       * Template method to do any Focusable related initialization that
       * does not involve event listeners creation.
       * @protected
       */
      initFocusable: Ext.emptyFn,
      
      /**
       * Template method to do any event listener initialization for a Focusable.
       * This generally happens after the focusEl is available.
       * @protected
       */
      initFocusableEvents: function(force) {
          // If *not* naturally focusable, then we look for the tabIndex property
          // to be defined which indicates that the element should be made focusable.
          this.initFocusableElement(force);
      },
  
      enableFocusable: Ext.emptyFn,
      
      disableFocusable: function() {
          var me = this;
          
          // If this is disabled while focused, by default, focus would return to document.body.
          // This must be avoided, both for the convenience of keyboard users, and also
          // for when focus is tracked within a tree, such as below an expanded ComboBox.
          if (me.hasFocus) {
              me.revertFocus();
          }
  
          me.removeFocusCls();
      },
  
      destroyFocusable: function() {
          var me = this;
          
          Ext.destroy(me.focusListeners);
          
          me.focusListeners = me.focusEnterEvent = me.focusTask = null;
          me.focusEl = me.ariaEl = null;
      },
      
      /**
       * Determine if this Focusable can receive focus at this time.
       *
       * Note that Containers can be non-focusable themselves while delegating
       * focus treatment to a child Component; see {@link Ext.Container #defaultFocus}
       * for more information.
       *
       * @param {Boolean} [deep=false] Optionally determine if the container itself
       * is focusable, or if container's focus is delegated to a child component
       * and that child is focusable.
       *
       * @return {Boolean} True if component is focusable, false if not.
       */
      isFocusable: function(deep) {
          var me = this,
              focusEl;
          
          if (!me.focusable && (!me.isContainer || !deep)) {
              return false;
          }
          
          focusEl = me.getFocusEl();
          
          if (focusEl && me.canFocus()) {
  
              // getFocusEl might return a Component if a Container wishes to
              // delegate focus to a descendant. Both Component and Element
              // implement isFocusable, so always ask that.
              return focusEl && !focusEl.destroyed && focusEl.isFocusable(deep);
          }
          
          return false;
      },
  
      /**
       * Determines if this Component is inside a Component tree which is destroyed, *or
       * is being destroyed*.
       * @return {boolean} `true` if this Component, or any ancestor is destroyed, or
       * is being destroyed.
       * @private
       */
      isDestructing: function() {
          for (var c = this; c; c = c.getRefOwner()) {
              if (c.destroying || c.destroyed) {
                  return true;
              }
          }
          return false;
      },
  
      canFocus: function(skipVisibility, includeFocusTarget) {
          var me = this,
              ownerFC = me.ownerFocusableContainer,
              focusableIfDisabled = ownerFC && ownerFC.allowFocusingDisabledChildren,
              canFocus;
          
          // Containers may have focusable children while being non-focusable
          // themselves; this is why we only account for me.focusable for
          // ordinary Components here and below.
          // MenuItems must accept focus when disabled.
          canFocus = !me.destroyed && me.rendered && !me.isDestructing() &&
                  (me.isContainer || me.focusable) &&
                  (!me.isDisabled() || focusableIfDisabled) &&
                  (skipVisibility || me.isVisible(true));
          
          return canFocus || (includeFocusTarget && !!me.findFocusTarget());
      },
      
      /**
       * Try to focus this component.
       *
       * If this component is disabled or otherwise not focusable, a close relation
       * will be targeted for focus instead to keep focus localized for keyboard users.
       *
       * @param {Boolean/Number[]} [selectText] If applicable, `true` to also select all the text
       * in this component, or an array consisting of start and end (defaults to start)
       * position of selection.
       *
       * @return {Boolean} `true` if focus target was found and focusing was attempted,
       * `false` if no focusing attempt was made.
       */
      focus: function(selectText) {
          var me = this,
              focusTarget, focusElDom;
  
          if ((!me.focusable && !me.isContainer) || me.destroyed || me.destroying) {
              return false;
          }
          
          // Assignment in conditional here to fall through to else block
          // if me.canFocus() returns true but there is no focus target
          if (me.canFocus() && (focusTarget = me.getFocusEl())) {
              // getFocusEl might return a child Widget or Component if a Container wishes
              // to delegate focus to a descendant via its defaultFocus configuration.
              if (focusTarget.$isFocusableEntity) {
                  return focusTarget.focus.apply(focusTarget, arguments);
              }
  
              focusElDom = focusTarget.dom;
  
              // This is an Element instance
              if (focusElDom) {
                  // NOT focusElDom.focus() here! Gotta run through possible Element overrides!
                  focusTarget.focus();
  
                  if (selectText && (me.selectText || focusElDom.select)) {
                      if (me.selectText) {
                          if (Ext.isArray(selectText)) {
                              me.selectText.apply(me, selectText);
                          }
                          else {
                              me.selectText();
                          }
                      }
                      else {
                          focusElDom.select();
                      }
                  }
              }
              // Could be a Widget or something else with no dom property but focusable
              else if (focusTarget.focus) {
                  focusTarget.focus();
              }
              else {
                  return false;
              }
          }
          else {
              // If we are asked to focus while not able to focus though disablement/invisibility etc,
              // focus may revert to document.body if the current focus is being hidden or destroyed.
              // This must be avoided, both for the convenience of keyboard users, and also
              // for when focus is tracked within a tree, such as below an expanded ComboBox.
              focusTarget = me.findFocusTarget();
              
              if (focusTarget && focusTarget != me) {
                  return focusTarget.focus.apply(focusTarget, arguments);
              }
              else {
                  return false;
              }
          }
  
          return true;
      },
  
      /**
       * @private
       */
      onBlur: function(e) {
          var me = this,
              container = me.ownerFocusableContainer;
          
          me.hasFocus = false;
          
          if (me.beforeBlur && !me.beforeBlur.$emptyFn) {
              me.beforeBlur(e);
          }
          
          if (container) {
              container.beforeFocusableChildBlur(me, e);
          }
          
          me.removeFocusCls(e);
          
          if (me.hasListeners.blur) {
              me.fireEvent('blur', me, e);
          }
          
          if (me.postBlur && !me.postBlur.$emptyFn) {
              me.postBlur(e);
          }
          
          if (container) {
              container.afterFocusableChildBlur(me, e);
          }
      },
  
      /**
       * @private
       */
      onFocus: function(e) {
          var me = this,
              container = me.ownerFocusableContainer;
  
          if (me.canFocus()) {
              if (me.beforeFocus && !me.beforeFocus.$emptyFn) {
                  me.beforeFocus(e);
              }
              
              if (container) {
                  container.beforeFocusableChildFocus(me, e);
              }
              
              me.addFocusCls(e);
              
              if (!me.hasFocus) {
                  me.hasFocus = true;
                  me.fireEvent('focus', me, e);
              }
              
              if (me.postFocus && !me.postFocus.$emptyFn) {
                  me.postFocus(e);
              }
              
              if (container) {
                  container.afterFocusableChildFocus(me, e);
              }
          }
      },
      
      /**
       * Return the actual tabIndex for this Focusable.
       *
       * @return {Number} tabIndex attribute value
       */
      getTabIndex: function() {
          var me = this,
              el, index;
          
          if (!me.focusable) {
              return;
          }
          
          el = me.getFocusEl();
          
          if (el) {
              // getFocusEl may return a child Widget or Component
              if (el.$isFocusableEntity) {
                  index = el.getTabIndex();
              }
              
              else if (el.isElement && el.dom) {
                  // We can't query el.dom.tabIndex because IE8 will return 0
                  // when tabIndex attribute is not present, and Chrome will
                  // return -1. Can't trust a browser to do a simplest thing. :/
                  index = el.dom.getAttribute('tabIndex');
                  
                  // This contraption is here because we can't simply coerce
                  // the returned attribute value to a number. If the attribute
                  // is not present, the value returned will be null, and coercing
                  // null gives 0. Wondrous JavaScript. :/
                  if (index !== null) {
                      index -= 0;
                  }
              }
              
              // A component can be configured with el: '#id' to look up
              // its main element from the DOM rather than render it; in
              // such case getTabIndex() may happen to be called before
              // said lookup has happened; indeterminate result follows.
              else {
                  return;
              }
          }
          
          if (typeof index !== 'number') {
              index = me.tabIndex;
          }
          
          return index; 
      },
      
      /**
       * Set the tabIndex property for this Focusable. If the focusEl
       * is available, set tabIndex attribute on it, too.
       *
       * @param {Number} newTabIndex new tabIndex to set
       * @param {HTMLElement} [focusEl] (private)
       */
      setTabIndex: function(newTabIndex, focusEl) {
          var me = this,
              ownerFC = me.ownerFocusableContainer,
              focusableIfDisabled = ownerFC && ownerFC.allowFocusingDisabledChildren,
              el;
          
          // See comments for definition of forceTabIndex as to why this is needed
          if (!me.focusable && !me.forceTabIndex) {
              return;
          }
          
          me.tabIndex = newTabIndex;
  
          // We must not do this if we are destroyed, or if we are incapable of being focused.
          // Handle our owning FocusableContainer having allowFocusingDisabledChildren
          if (me.destroying || me.destroyed || (me.isDisabled() && !focusableIfDisabled)) {
              return;
          }
          
          el = focusEl || me.getFocusEl();
          
          if (el) {
              // getFocusEl may return a child Widget or Component
              if (el.$isFocusableEntity) {
                  el.setTabIndex(newTabIndex);
              }
              
              // Or if a component is configured with el: '#id', it may
              // still be a string by the time setTabIndex is called from
              // owner FocusableContainer.
              else if (el.isElement && el.dom) {
                  // setTabIndex is aware of saved tabbable state
                  el.setTabIndex(newTabIndex);
              }
          }
      },
      
      /**
       * @template
       * @protected
       * Called when focus enters this Component's hierarchy
       * @param {Object} e
       * @param {Ext.event.Event} e.event The underlying DOM event.
       * @param {HTMLElement} e.target The element gaining focus.
       * @param {HTMLElement} e.relatedTarget The element losing focus.
       * @param {Ext.Component} e.toComponent The Component gaining focus.
       * @param {Ext.Component} e.fromComponent The Component losing focus.
       * @param {Boolean} e.backwards `true` if the `fromComponent` is *after* the `toComponent*
       * in the DOM tree, indicating that the user used `SHIFT+TAB` to move focus. Note that setting
       * `tabIndex` values to affect tabbing order can cause this to be incorrect. Setting
       * `tabIndex` values is not advised.
       */
      onFocusEnter: function(e) {
          var me = this;
          
          // We DO NOT check if `me` is focusable here. The reason is that
          // non-focusable containers need to track focus entering their
          // children so that revertFocus would work if these children
          // become unavailable.
          if (me.destroying || me.destroyed) {
              return;
          }
  
          // Save all information about how we received focus so that
          // we can do appropriate things when asked to revertFocus
          me.focusEnterEvent = e;
          me.containsFocus = true;
          
          if (me.hasListeners.focusenter) {
              me.fireEvent('focusenter', me, e);
          }
      },
  
      /**
       * @template
       * @protected
       * Called when focus exits from this Component's hierarchy
       * @param {Ext.event.Event} e
       * @param {Ext.event.Event} e.event The underlying DOM event.
       * @param {HTMLElement} e.target The element gaining focus.
       * @param {HTMLElement} e.relatedTarget The element losing focus.
       * @param {Ext.Component} e.toComponent The Component gaining focus.
       * @param {Ext.Component} e.fromComponent The Component losing focus.
       */
      onFocusLeave: function(e) {
          var me = this;
  
          // Same as in onFocusEnter
          if (me.destroying || me.destroyed) {
              return;
          }
  
          me.focusEnterEvent = null;
          me.containsFocus = false;
          
          if (me.hasListeners.focusleave) {
              me.fireEvent('focusleave', me, e);
          }
      },
  
      /**
       * @template
       * @method
       * @protected
       * Called when focus moves *within* this Component's hierarchy
       * @param {Object} info
       * @param {Ext.event.Event} info.event The underlying Event object.
       * @param {HTMLElement} info.toElement The element gaining focus.
       * @param {HTMLElement} info.fromElement The element losing focus.
       * @param {Ext.Component} info.toComponent The Component gaining focus.
       * @param {Ext.Component} info.fromComponent The Component losing focus.
       * @param {Boolean} info.backwards `true` if the focus movement is backward in DOM order
       */
      onFocusMove: Ext.emptyFn,
  
      privates: {
          // This private flag was introduced to work around an issue where
          // the tab index would not be stamped onto the component.
          // Consider a tool, which is focusable by default and has tabIndex: 0
          // on the class definition. If a non-focusable tool is required, setting
          // focusable: false is not enough, the tabIndex also needs to be considered.
          // However, the default behaviour is to not modify the tabIndex on non
          // focusable items. This config can go away if that behaviour is changed.
          // Arguably, a non-focusable widget probably shouldn't retain a tab index
          // if it's explicitly configured.
          forceTabIndex: false,
          
          /**
           * Returns focus to the Component or element found in the cached
           * focusEnterEvent.
           *
           * @private
           */
          revertFocus: function() {
              var me = this,
                  focusEvent = me.focusEnterEvent,
                  activeElement = Ext.Element.getActiveElement(),
                  focusTarget, fromComponent, reverted;
  
              // If we have a record of where focus arrived from,
              //  and have not been told to avoid refocusing,
              //  and we contain the activeElement.
              // Then, before hiding, restore focus to what was focused before we were focused.
              if (focusEvent && !me.preventRefocus && me.el.contains(activeElement)) {
  
                  fromComponent = focusEvent.fromComponent;
  
                  // If the default focus reversion target is in shifting ground, fall back to document.body
                  if (fromComponent && (fromComponent.destroyed || fromComponent.isDestructing())) {
                      focusTarget = document.body;
                  }
  
                  // Preferred focus target is the actual element from which focus entered this component.
                  // It will be up to its encapsulating component to handle this in an appropriate way.
                  // For example, a grid, upon having focus pushed to a certain cell will set its
                  // navigation position to that cell and highlight it as focused.
                  // Likewise an input field must handle its field acquiring focus.
                  else {
                      focusTarget = focusEvent.relatedTarget;
                  }
  
                  // If focus was from the body, try to keep it closer than that
                  if (focusTarget === document.body) {
                      fromComponent = me.findFocusTarget();
                      if (fromComponent) {
                          focusTarget = fromComponent.getFocusEl();
                      }
                  }
  
                  if (focusTarget && focusTarget.$isFocusableEntity) {
                      if (!focusTarget.destroyed && focusTarget.isFocusable()) {
                          focusTarget.focus();
                      }
                  }
                  
                  // If the element is in the document and focusable, then we're good. The owning component will handle it.
                  else if (Ext.getDoc().contains(focusTarget) && Ext.fly(focusTarget).isFocusable()) {
                      fromComponent = Ext.Component.from(focusTarget);
  
                      // Allow the focus recieving component to modify the focus sequence.
                      if (fromComponent) {
                          fromComponent.revertFocusTo(focusTarget);
                      } else {
                          focusTarget.focus();
                      }
                  }
  
                  // If the element has gone, or is hidden, we will have to rely on the intelligent focus diversion
                  // of components to send focus back to somewhere that is least surprising for the user.
                  else if (focusEvent.fromComponent && focusEvent.fromComponent.focus) {
                      reverted = focusEvent.fromComponent.focus();
  
                      // The component was not able to find a suitable target.
                      // Important: Touch platforms do not blur programatically focused elements
                      // when they become hidden, so we must force the issue in order to maintain
                      // focus tracking.
                      if (!reverted) {
                          activeElement.blur();
                      }
                  }
              }
          },
  
          /**
           * This field is on the recieving end of a call from {@link #method!revertFocus}.
           *
           * It is called when focus is being pushed back into this Component from a Component
           * that is focused and is being hidden or disabled.
           *
           * We must focus the passed element.
           *
           * Subclasses may perform some extra processing to prepare for refocusing.
           * @param target
           */
          revertFocusTo: function(target) {
              target.focus();
          },
  
          /**
           * Finds an alternate Component to focus if this Component is disabled while focused, or
           * focused while disabled, or otherwise unable to focus.
           * 
           * In both cases, focus must not be lost to document.body, but must move to an intuitively
           * connectible Component, either a sibling, or uncle or nephew.
           *
           * This is both for the convenience of keyboard users, and also for when focus is tracked
           * within a Component tree such as for ComboBoxes and their dropdowns.
           *
           * For example, a ComboBox with a PagingToolbar in is BoundList. If the "Next Page"
           * button is hit, the LoadMask shows and focuses, the next page is the last page, so
           * the "Next Page" button is disabled. When the LoadMask hides, it attempt to focus the
           * last focused Component which is the disabled "Next Page" button. In this situation,
           * focus should move to a sibling within the PagingToolbar.
           * 
           * @return {Ext.Component} A closely related focusable Component to which focus can move.
           * @private
           */
          findFocusTarget: function() {
              var me = this,
                  parentAxis, candidate, len, i, focusTargets, focusIndex;
              
              if (me.preventRefocus) {
                  return null;
              }
  
              // Create an axis of visible, enabled, stable parent components which we
              // will walk up looking for ancestors to revert focus to.
              //
              // First, find all enabled parents.
              for (parentAxis = [], candidate = me.getRefOwner(); candidate; candidate = candidate.getRefOwner()) {
                  if (!candidate.isDisabled()) {
                      parentAxis.unshift(candidate);
                  }
              }
  
              // Then walk downwards until we encounter a non-targetable parent
              // which means hidden of destroying. All candidates *above* that
              // are potential sources of focus targets.
              for (i = 0, len = parentAxis.length; i < len; i++) {
                  candidate = parentAxis[i];
                  if (candidate.destroying || !candidate.isVisible()) {
                      parentAxis.length = i;
                      break;
                  }
              }
  
              // Walk up the parent axis checking each parent for focus targets.
              for (i = parentAxis.length - 1; i >=0; i--) {
                  candidate = parentAxis[i];
                  // Use CQ to find a target that is fully focusable (:canfocus, NOT the theoretical :focusable)
                  // Cannot use :focusable(true) because that consults findFocusTarget and would cause infinite recursion.
                  // Exclude the component which currently has focus.
                  // Cannot use candidate.child() because the parent might not be a Container.
                  // Non-Container Components may still have ownership relationships with
                  // other Components. eg: BoundList with PagingToolbar
                  focusTargets = Ext.ComponentQuery.query(':canfocus()', candidate);
                  if (focusTargets.length) {
                      focusIndex = Ext.Array.indexOf(focusTargets, Ext.ComponentManager.getActiveComponent());
  
                      // Return the next focusable, or the previous focusable, or the first focusable
                      return focusTargets[focusIndex + 1] || focusTargets[focusIndex - 1]
                          || focusTargets[0];
                  }
  
                  // We found no focusable siblings in our candidate, but the candidate may itself be focusable,
                  // it is not always a Container - could be the owning Field of a BoundList.
                  if (candidate.isFocusable && candidate.isFocusable()) {
                      return candidate;
                  }
              }
          },
      
          /**
           * Sets up the focus listener on this Component's {@link #getFocusEl focusEl} if it has one.
           *
           * Form Components which must implicitly participate in tabbing order usually have a naturally
           * focusable element as their {@link #getFocusEl focusEl}, and it is the DOM event of that
           * receiving focus which drives the Component's `onFocus` handling, and the DOM event of it
           * being blurred which drives the `onBlur` handling.
           * @private
           */
          initFocusableElement: function(force) {
              var me = this,
                  tabIndex = me.getTabIndex(),
                  focusEl = me.getFocusEl();
  
              if (focusEl && !focusEl.$isFocusableEntity) {
                  // focusEl is not available until after rendering, and rendering tabIndex
                  // into focusEl is not always convenient. So we apply it here if Component's
                  // tabIndex property is set and Component is otherwise focusable.
                  // Note that for Widgets and Modern Components we might not have the rendered
                  // flag set yet but can force setting the property.
                  if (tabIndex != null && (force || me.canFocus(true))) {
                      me.setTabIndex(tabIndex, focusEl);
                  }
                  
                  // This attribute is a shortcut to look up a Component by its Elements
                  // It only makes sense on focusable elements, so we set it here unless
                  // our focusEl is delegated to the focusEl of an owned Component and it
                  // already has ownership stamped into it.
                  if (!focusEl.dom.hasAttribute('data-componentid')) {
                      focusEl.dom.setAttribute('data-componentid', me.id);
                  }
              }
          },
          
          addFocusCls: function(e) {
              var focusCls = this.focusCls,
                  el;
              
              el = this.getFocusClsEl();
              
              if (focusCls) {
                  el = this.getFocusClsEl(e);
                  
                  if (el) {
                      el.addCls(focusCls);
                  }
              }
          },
          
          removeFocusCls: function(e) {
              var focusCls = this.focusCls,
                  el;
              
              if (focusCls) {
                  el = this.getFocusClsEl(e);
                  
                  if (el) {
                      el.removeCls(focusCls);
                  }
              }
          },
          
          /**
           * @private
           */
          handleFocusEvent: function(e) {
              var me = this,
                  event;
              
              if (!me.focusable || me.destroying || me.destroyed) {
                  return;
              }
              
              // handleFocusEvent and handleBlurEvent are called by ComponentManager
              // passing the normalized element event that might or might not cause
              // component focus or blur. The component itself makes the decision
              // whether focus/blur happens or not. This is necessary for components
              // that might have more than one focusable element within the component's
              // DOM structure, like Ext.button.Split.
              if (me.isFocusing(e)) {
                  event = new Ext.event.Event(e.event);
                  event.type = 'focus';
                  event.relatedTarget = e.fromElement;
                  event.target = e.toElement;
                  
                  me.onFocus(event);
              }
          },
          
          /**
           * @private
           */
          handleBlurEvent: function(e) {
              var me = this,
                  event;
              
              if (!me.focusable || me.destroying || me.destroyed) {
                  return;
              }
  
              // Must process blur if toElement is document.body
              // Blurs caused by the app losing focus are processed synchronously
              // for obvious reasons, so the activeElement will still be the
              // focusEl, so isBlurring will return false, and reject this op.
              if (e.toElement === document.body || me.isBlurring(e)) {
                  event = new Ext.event.Event(e.event);
                  event.type = 'blur';
                  event.target = e.fromElement;
                  event.relatedTarget = e.toElement;
                  
                  me.onBlur(event);
              }
          },
          
          /**
           * @private
           */
          isFocusing: function(e) {
              var focusEl = this.getFocusEl();
          
              if (focusEl) {
                  if (focusEl.isFocusing) {
                      return focusEl.isFocusing(e);
                  }
                  else {
                      // Sometimes focusing an element may cause reaction from other entities
                      // that will focus something else instead. So before unraveling the
                      // event chain we better make sure our focusEl is *indeed* focused.
                      return focusEl.dom === document.activeElement &&
                             e.toElement === focusEl.dom && e.fromElement !== e.toElement;
                  }
              }
              
              return false;
          },
          
          /**
           * @private
           */
          isBlurring: function(e) {
              var focusEl = this.getFocusEl();
          
              if (focusEl) {
                  if (focusEl.isFocusing) {
                      return focusEl.isBlurring(e);
                  }
                  else {
                      // Ditto for blurring: in some cases freshly blurred element can be
                      // refocused by external forces so we need to check if our focusEl
                      // *indeed* is not focused before we can call it blurring.
                      return focusEl.dom !== document.activeElement &&
                             e.fromElement === focusEl.dom && e.fromElement !== e.toElement;
                  }
              }
              
              return false;
          },
          
          /**
           * @private
           */
          blur: function() {
              var me = this,
                  focusEl;
              
              if (!me.focusable || !me.canFocus()) {
                  return;
              }
              
              focusEl = me.getFocusEl();
  
              if (focusEl) {
                  me.blurring = true;
                  focusEl.blur();
                  delete me.blurring;
              }
          },
          
          isTabbable: function() {
              var me = this,
                  focusEl;
              
              if (me.focusable) {
                  focusEl = me.getFocusEl();
                  
                  if (focusEl && focusEl.isTabbable()) {
                      return focusEl.isTabbable();
                  }
              }
              
              return false;
          },
          
          disableTabbing: function() {
              var me = this,
                  el = me.el,
                  focusEl;
              
              // We DO NOT check for me.focusable here, because this should work
              // for containers with focus delegates, too!
              if (me.destroying || me.destroyed) {
                  return;
              }
              
              // We're disabling tabbability for all elements of a given Component;
              // focusEl might be outside of the hierarchy which is checked below.
              if (el) {
                  el.saveTabbableState();
              }
              
              focusEl = me.getFocusEl();
              
              if (focusEl) {
                  // focusEl may happen to be a focus delegate for a container
                  if (focusEl.$isFocusableEntity) {
                      focusEl.disableTabbing();
                  }
                  
                  // Alternatively focusEl may happen to be outside of the main el,
                  // or else it can be a string reference to an element that
                  // has not been resolved yet
                  else if (focusEl.isElement && el && !el.contains(focusEl)) {
                      focusEl.saveTabbableState();
                  }
              }
          },
          
          enableTabbing: function(reset) {
              var me = this,
                  el = me.el,
                  focusEl;
              
              // We DO NOT check for me.focusable here, because this should work
              // for containers with focus delegates, too!
              if (me.destroying || me.destroyed) {
                  return;
              }
              
              focusEl = me.getFocusEl();
              
              if (focusEl) {
                  if (focusEl.$isFocusableEntity) {
                      focusEl.enableTabbing();
                  }
                  else if (focusEl.isElement && el && !el.contains(focusEl)) {
                      focusEl.restoreTabbableState();
                  }
              }
              
              if (el) {
                  el.restoreTabbableState({ reset: reset });
              }
          }
      }
  }, function() {
      var keyboardModeCls = Ext.baseCSSPrefix + 'keyboard-mode',
          keyboardMode = false;
  
      /**
       * @property {Boolean} keyboardMode
       * @member Ext
       * A flag which indicates that the last UI interaction from the user was a keyboard gesture
       */
  
      /**
       * @property {Boolean} touchMode
       * @member Ext
       * A flag which indicates that the last UI interaction from the user was a touch gesture
       */
  
      Ext.setKeyboardMode = Ext.setKeyboardMode || function (keyboardMode) {
          Ext.keyboardMode = keyboardMode;
          Ext.getBody().toggleCls(keyboardModeCls, keyboardMode);
      };
  
      Ext.isTouchMode = function () {
          return (Ext.now() - Ext.lastTouchTime) < 500;
      };
  
      function syncKeyboardMode(e) {
          var type = e.type;
  
          if (type === 'pointermove') {
              // On pointer move we want to track that the user has switched from keyboard
              // to mouse, but we do not remove the visual focus style until a pointerdown
              // occurs or the focus changes.  This accomodates menus which change focus
              // based on mouseenter of a menu item
              keyboardMode = false;
          } else {
              keyboardMode = (type === 'keydown');
              Ext.lastTouchTime = e.pointerType === 'touch' && Ext.now();
              Ext.setKeyboardMode(keyboardMode);
          }
      }
  
      Ext.onReady(function() {
          Ext.getWin().on({
              pointerdown: syncKeyboardMode,
              pointermove: syncKeyboardMode,
              keydown: syncKeyboardMode,
              capture: true,
              delegated: false
          });
  
          Ext.on('focus', function() {
              if (keyboardMode !== Ext.getBody().hasCls(keyboardModeCls)) {
                  Ext.setKeyboardMode(keyboardMode);
              }
          });
      });
  });
  