[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/node-focusmanager/ -> node-focusmanager-debug.js (source)

   1  /*
   2  YUI 3.17.2 (build 9c3c78e)
   3  Copyright 2014 Yahoo! Inc. All rights reserved.
   4  Licensed under the BSD License.
   5  http://yuilibrary.com/license/
   6  */
   7  
   8  YUI.add('node-focusmanager', function (Y, NAME) {
   9  
  10  /**
  11  * <p>The Focus Manager Node Plugin makes it easy to manage focus among
  12  * a Node's descendants.  Primarily intended to help with widget development,
  13  * the Focus Manager Node Plugin can be used to improve the keyboard
  14  * accessibility of widgets.</p>
  15  *
  16  * <p>
  17  * When designing widgets that manage a set of descendant controls (i.e. buttons
  18  * in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to
  19  * limit the number of descendants in the browser's default tab flow.  The fewer
  20  * number of descendants in the default tab flow, the easier it is for keyboard
  21  * users to navigate between widgets by pressing the tab key.  When a widget has
  22  * focus it should provide a set of shortcut keys (typically the arrow keys)
  23  * to move focus among its descendants.
  24  * </p>
  25  *
  26  * <p>
  27  * To this end, the Focus Manager Node Plugin makes it easy to define a Node's
  28  * focusable descendants, define which descendant should be in the default tab
  29  * flow, and define the keys that move focus among each descendant.
  30  * Additionally, as the CSS
  31  * <a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
  32  * pseudo class is not supported on all elements in all
  33  * <a href="http://developer.yahoo.com/yui/articles/gbs/">A-Grade browsers</a>,
  34  * the Focus Manager Node Plugin provides an easy, cross-browser means of
  35  * styling focus.
  36  * </p>
  37  *
  38  
  39  DEPRECATED: The FocusManager Node Plugin has been deprecated as of YUI 3.9.0. This module will be removed from the library in a future version. If you require functionality similar to the one provided by this  module, consider taking a look at the various modules in the YUI Gallery <http://yuilibrary.com/gallery/>.
  40  
  41  * @module node-focusmanager
  42  * @deprecated 3.9.0
  43  */
  44  
  45      //    Frequently used strings
  46  
  47  var ACTIVE_DESCENDANT = "activeDescendant",
  48      ID = "id",
  49      DISABLED = "disabled",
  50      TAB_INDEX = "tabIndex",
  51      FOCUSED = "focused",
  52      FOCUS_CLASS = "focusClass",
  53      CIRCULAR = "circular",
  54      UI = "UI",
  55      KEY = "key",
  56      ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
  57      HOST = "host",
  58  
  59      //    Collection of keys that, when pressed, cause the browser viewport
  60      //    to scroll.
  61      scrollKeys = {
  62          37: true,
  63          38: true,
  64          39: true,
  65          40: true
  66      },
  67  
  68      clickableElements = {
  69          "a": true,
  70          "button": true,
  71          "input": true,
  72          "object": true
  73      },
  74  
  75      //    Library shortcuts
  76  
  77      Lang = Y.Lang,
  78       UA = Y.UA,
  79  
  80      /**
  81      * The NodeFocusManager class is a plugin for a Node instance.  The class is used
  82      * via the <a href="Node.html#method_plug"><code>plug</code></a> method of Node
  83      * and should not be instantiated directly.
  84      * @namespace plugin
  85      * @class NodeFocusManager
  86      */
  87      NodeFocusManager = function () {
  88  
  89          NodeFocusManager.superclass.constructor.apply(this, arguments);
  90  
  91      };
  92  
  93  
  94  NodeFocusManager.ATTRS = {
  95  
  96      /**
  97      * Boolean indicating that one of the descendants is focused.
  98      *
  99      * @attribute focused
 100      * @readOnly
 101      * @default false
 102      * @type boolean
 103      */
 104      focused: {
 105  
 106          value: false,
 107          readOnly: true
 108  
 109      },
 110  
 111  
 112      /**
 113      * String representing the CSS selector used to define the descendant Nodes
 114      * whose focus should be managed.
 115      *
 116      * @attribute descendants
 117      * @type Y.NodeList
 118      */
 119      descendants: {
 120  
 121          getter: function (value) {
 122  
 123              return this.get(HOST).all(value);
 124  
 125          }
 126  
 127      },
 128  
 129  
 130      /**
 131      * <p>Node, or index of the Node, representing the descendant that is either
 132      * focused or is focusable (<code>tabIndex</code> attribute is set to 0).
 133      * The value cannot represent a disabled descendant Node.  Use a value of -1
 134      * to remove all descendant Nodes from the default tab flow.
 135      * If no value is specified, the active descendant will be inferred using
 136      * the following criteria:</p>
 137      * <ol>
 138      * <li>Examining the <code>tabIndex</code> attribute of each descendant and
 139      * using the first descendant whose <code>tabIndex</code> attribute is set
 140      * to 0</li>
 141      * <li>If no default can be inferred then the value is set to either 0 or
 142      * the index of the first enabled descendant.</li>
 143      * </ol>
 144      *
 145      * @attribute activeDescendant
 146      * @type Number
 147      */
 148      activeDescendant: {
 149  
 150          setter: function (value) {
 151  
 152              var isNumber = Lang.isNumber,
 153                  INVALID_VALUE = Y.Attribute.INVALID_VALUE,
 154                  descendantsMap = this._descendantsMap,
 155                  descendants = this._descendants,
 156                  nodeIndex,
 157                  returnValue,
 158                  oNode;
 159  
 160  
 161              if (isNumber(value)) {
 162                  nodeIndex = value;
 163                  returnValue = nodeIndex;
 164              }
 165              else if ((value instanceof Y.Node) && descendantsMap) {
 166  
 167                  nodeIndex = descendantsMap[value.get(ID)];
 168  
 169                  if (isNumber(nodeIndex)) {
 170                      returnValue = nodeIndex;
 171                  }
 172                  else {
 173  
 174                      //    The user passed a reference to a Node that wasn't one
 175                      //    of the descendants.
 176                      returnValue = INVALID_VALUE;
 177  
 178                  }
 179  
 180              }
 181              else {
 182                  returnValue = INVALID_VALUE;
 183              }
 184  
 185  
 186              if (descendants) {
 187  
 188                  oNode = descendants.item(nodeIndex);
 189  
 190                  if (oNode && oNode.get("disabled")) {
 191  
 192                      //    Setting the "activeDescendant" attribute to the index
 193                      //    of a disabled descendant is invalid.
 194                      returnValue = INVALID_VALUE;
 195  
 196                  }
 197  
 198              }
 199  
 200  
 201              return returnValue;
 202  
 203          }
 204  
 205      },
 206  
 207  
 208      /**
 209      * Object literal representing the keys to be used to navigate between the
 210      * next/previous descendant.  The format for the attribute's value is
 211      * <code>{ next: "down:40", previous: "down:38" }</code>.  The value for the
 212      * "next" and "previous" properties are used to attach
 213      * <a href="event/#keylistener"><code>key</code></a> event listeners. See
 214      * the <a href="event/#keylistener">Using the key Event</a> section of
 215      * the Event documentation for more information on "key" event listeners.
 216      *
 217      * @attribute keys
 218      * @type Object
 219      */
 220      keys: {
 221  
 222          value: {
 223  
 224              next: null,
 225              previous: null
 226  
 227          }
 228  
 229  
 230      },
 231  
 232  
 233      /**
 234      * String representing the name of class applied to the focused active
 235      * descendant Node.  Can also be an object literal used to define both the
 236      * class name, and the Node to which the class should be applied.  If using
 237      * an object literal, the format is:
 238      * <code>{ className: "focus", fn: myFunction }</code>.  The function
 239      * referenced by the <code>fn</code> property in the object literal will be
 240      * passed a reference to the currently focused active descendant Node.
 241      *
 242      * @attribute focusClass
 243      * @type String|Object
 244      */
 245      focusClass: { },
 246  
 247  
 248      /**
 249      * Boolean indicating if focus should be set to the first/last descendant
 250      * when the end or beginning of the descendants has been reached.
 251      *
 252      * @attribute circular
 253      * @type Boolean
 254      * @default true
 255      */
 256      circular: {
 257          value: true
 258      }
 259  
 260  };
 261  
 262  Y.extend(NodeFocusManager, Y.Plugin.Base, {
 263  
 264      //    Protected properties
 265  
 266      //    Boolean indicating if the NodeFocusManager is active.
 267      _stopped: true,
 268  
 269      //    NodeList representing the descendants selected via the
 270      //    "descendants" attribute.
 271      _descendants: null,
 272  
 273      //    Object literal mapping the IDs of each descendant to its index in the
 274      //    "_descendants" NodeList.
 275      _descendantsMap: null,
 276  
 277      //    Reference to the Node instance to which the focused class (defined
 278      //    by the "focusClass" attribute) is currently applied.
 279      _focusedNode: null,
 280  
 281      //    Number representing the index of the last descendant Node.
 282      _lastNodeIndex: 0,
 283  
 284      //    Array of handles for event handlers used for a NodeFocusManager instance.
 285      _eventHandlers: null,
 286  
 287  
 288  
 289      //    Protected methods
 290  
 291      /**
 292      * @method _initDescendants
 293      * @description Sets the <code>tabIndex</code> attribute of all of the
 294      * descendants to -1, except the active descendant, whose
 295      * <code>tabIndex</code> attribute is set to 0.
 296      * @protected
 297      */
 298      _initDescendants: function () {
 299  
 300          var descendants = this.get("descendants"),
 301              descendantsMap = {},
 302              nFirstEnabled = -1,
 303              nDescendants,
 304              nActiveDescendant = this.get(ACTIVE_DESCENDANT),
 305              oNode,
 306              sID,
 307              i = 0;
 308  
 309  
 310  
 311          if (Lang.isUndefined(nActiveDescendant)) {
 312              nActiveDescendant = -1;
 313          }
 314  
 315  
 316          if (descendants) {
 317  
 318              nDescendants = descendants.size();
 319  
 320  
 321              for (i = 0; i < nDescendants; i++) {
 322  
 323                  oNode = descendants.item(i);
 324  
 325                  if (nFirstEnabled === -1 && !oNode.get(DISABLED)) {
 326                      nFirstEnabled = i;
 327                  }
 328  
 329  
 330                  //    If the user didn't specify a value for the
 331                  //    "activeDescendant" attribute try to infer it from
 332                  //    the markup.
 333  
 334                  //    Need to pass "2" when using "getAttribute" for IE to get
 335                  //    the attribute value as it is set in the markup.
 336                  //    Need to use "parseInt" because IE always returns the
 337                  //    value as a number, whereas all other browsers return
 338                  //    the attribute as a string when accessed
 339                  //    via "getAttribute".
 340  
 341                  if (nActiveDescendant < 0 &&
 342                          parseInt(oNode.getAttribute(TAB_INDEX, 2), 10) === 0) {
 343  
 344                      nActiveDescendant = i;
 345  
 346                  }
 347  
 348                  if (oNode) {
 349                      oNode.set(TAB_INDEX, -1);
 350                  }
 351  
 352                  sID = oNode.get(ID);
 353  
 354                  if (!sID) {
 355                      sID = Y.guid();
 356                      oNode.set(ID, sID);
 357                  }
 358  
 359                  descendantsMap[sID] = i;
 360  
 361              }
 362  
 363  
 364              //    If the user didn't specify a value for the
 365              //    "activeDescendant" attribute and no default value could be
 366              //    determined from the markup, then default to 0.
 367  
 368              if (nActiveDescendant < 0) {
 369                  nActiveDescendant = 0;
 370              }
 371  
 372  
 373              oNode = descendants.item(nActiveDescendant);
 374  
 375              //    Check to make sure the active descendant isn't disabled,
 376              //    and fall back to the first enabled descendant if it is.
 377  
 378              if (!oNode || oNode.get(DISABLED)) {
 379                  oNode = descendants.item(nFirstEnabled);
 380                  nActiveDescendant = nFirstEnabled;
 381              }
 382  
 383              this._lastNodeIndex = nDescendants - 1;
 384              this._descendants = descendants;
 385              this._descendantsMap = descendantsMap;
 386  
 387              this.set(ACTIVE_DESCENDANT, nActiveDescendant);
 388  
 389              //    Need to set the "tabIndex" attribute here, since the
 390              //    "activeDescendantChange" event handler used to manage
 391              //    the setting of the "tabIndex" attribute isn't wired up yet.
 392  
 393              if (oNode) {
 394                  oNode.set(TAB_INDEX, 0);
 395              }
 396  
 397          }
 398  
 399      },
 400  
 401  
 402      /**
 403      * @method _isDescendant
 404      * @description Determines if the specified Node instance is a descendant
 405      * managed by the Focus Manager.
 406      * @param node {Node} Node instance to be checked.
 407      * @return {Boolean} Boolean indicating if the specified Node instance is a
 408      * descendant managed by the Focus Manager.
 409      * @protected
 410      */
 411      _isDescendant: function (node) {
 412  
 413          return (node.get(ID) in this._descendantsMap);
 414  
 415      },
 416  
 417  
 418      /**
 419      * @method _removeFocusClass
 420      * @description Removes the class name representing focus (as specified by
 421      * the "focusClass" attribute) from the Node instance to which it is
 422      * currently applied.
 423      * @protected
 424      */
 425      _removeFocusClass: function () {
 426  
 427          var oFocusedNode = this._focusedNode,
 428              focusClass = this.get(FOCUS_CLASS),
 429              sClassName;
 430  
 431          if (focusClass) {
 432              sClassName = Lang.isString(focusClass) ?
 433                  focusClass : focusClass.className;
 434          }
 435  
 436          if (oFocusedNode && sClassName) {
 437              oFocusedNode.removeClass(sClassName);
 438          }
 439  
 440      },
 441  
 442  
 443      /**
 444      * @method _detachKeyHandler
 445      * @description Detaches the "key" event handlers used to support the "keys"
 446      * attribute.
 447      * @protected
 448      */
 449      _detachKeyHandler: function () {
 450  
 451          var prevKeyHandler = this._prevKeyHandler,
 452              nextKeyHandler = this._nextKeyHandler;
 453  
 454          if (prevKeyHandler) {
 455              prevKeyHandler.detach();
 456          }
 457  
 458          if (nextKeyHandler) {
 459              nextKeyHandler.detach();
 460          }
 461  
 462      },
 463  
 464  
 465      /**
 466      * @method _preventScroll
 467      * @description Prevents the viewport from scolling when the user presses
 468      * the up, down, left, or right key.
 469      * @protected
 470      */
 471      _preventScroll: function (event) {
 472  
 473          if (scrollKeys[event.keyCode] && this._isDescendant(event.target)) {
 474              event.preventDefault();
 475          }
 476  
 477      },
 478  
 479  
 480      /**
 481      * @method _fireClick
 482      * @description Fires the click event if the enter key is pressed while
 483      * focused on an HTML element that is not natively clickable.
 484      * @protected
 485      */
 486      _fireClick: function (event) {
 487  
 488          var oTarget = event.target,
 489              sNodeName = oTarget.get("nodeName").toLowerCase();
 490  
 491          if (event.keyCode === 13 && (!clickableElements[sNodeName] ||
 492                  (sNodeName === "a" && !oTarget.getAttribute("href")))) {
 493  
 494              Y.log(("Firing click event for node:" + oTarget.get("id")), "info", "nodeFocusManager");
 495  
 496              oTarget.simulate("click");
 497  
 498          }
 499  
 500      },
 501  
 502  
 503      /**
 504      * @method _attachKeyHandler
 505      * @description Attaches the "key" event handlers used to support the "keys"
 506      * attribute.
 507      * @protected
 508      */
 509      _attachKeyHandler: function () {
 510  
 511          this._detachKeyHandler();
 512  
 513          var sNextKey = this.get("keys.next"),
 514              sPrevKey = this.get("keys.previous"),
 515              oNode = this.get(HOST),
 516              aHandlers = this._eventHandlers;
 517  
 518          if (sPrevKey) {
 519               this._prevKeyHandler =
 520                  Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey);
 521          }
 522  
 523          if (sNextKey) {
 524               this._nextKeyHandler =
 525                  Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey);
 526          }
 527  
 528  
 529          //    In Opera it is necessary to call the "preventDefault" method in
 530          //    response to the user pressing the arrow keys in order to prevent
 531          //    the viewport from scrolling when the user is moving focus among
 532          //    the focusable descendants.
 533  
 534          if (UA.opera) {
 535              aHandlers.push(oNode.on("keypress", this._preventScroll, this));
 536          }
 537  
 538  
 539          //    For all browsers except Opera: HTML elements that are not natively
 540          //    focusable but made focusable via the tabIndex attribute don't
 541          //    fire a click event when the user presses the enter key.  It is
 542          //    possible to work around this problem by simplying dispatching a
 543          //    click event in response to the user pressing the enter key.
 544  
 545          if (!UA.opera) {
 546              aHandlers.push(oNode.on("keypress", this._fireClick, this));
 547          }
 548  
 549      },
 550  
 551  
 552      /**
 553      * @method _detachEventHandlers
 554      * @description Detaches all event handlers used by the Focus Manager.
 555      * @protected
 556      */
 557      _detachEventHandlers: function () {
 558  
 559          this._detachKeyHandler();
 560  
 561          var aHandlers = this._eventHandlers;
 562  
 563          if (aHandlers) {
 564  
 565              Y.Array.each(aHandlers, function (handle) {
 566                  handle.detach();
 567              });
 568  
 569              this._eventHandlers = null;
 570  
 571          }
 572  
 573      },
 574  
 575  
 576      /**
 577      * @method _detachEventHandlers
 578      * @description Attaches all event handlers used by the Focus Manager.
 579      * @protected
 580      */
 581      _attachEventHandlers: function () {
 582  
 583          var descendants = this._descendants,
 584              aHandlers,
 585              oDocument,
 586              handle;
 587  
 588          if (descendants && descendants.size()) {
 589  
 590              aHandlers = this._eventHandlers || [];
 591              oDocument = this.get(HOST).get("ownerDocument");
 592  
 593  
 594              if (aHandlers.length === 0) {
 595  
 596                  Y.log("Attaching base set of event handlers.", "info", "nodeFocusManager");
 597  
 598                  aHandlers.push(oDocument.on("focus", this._onDocFocus, this));
 599  
 600                  aHandlers.push(oDocument.on("mousedown",
 601                      this._onDocMouseDown, this));
 602  
 603                  aHandlers.push(
 604                          this.after("keysChange", this._attachKeyHandler));
 605  
 606                  aHandlers.push(
 607                          this.after("descendantsChange", this._initDescendants));
 608  
 609                  aHandlers.push(
 610                          this.after(ACTIVE_DESCENDANT_CHANGE,
 611                                  this._afterActiveDescendantChange));
 612  
 613  
 614                  //    For performance: defer attaching all key-related event
 615                  //    handlers until the first time one of the specified
 616                  //    descendants receives focus.
 617  
 618                  handle = this.after("focusedChange", Y.bind(function (event) {
 619  
 620                      if (event.newVal) {
 621  
 622                          Y.log("Attaching key event handlers.", "info", "nodeFocusManager");
 623  
 624                          this._attachKeyHandler();
 625  
 626                          //    Detach this "focusedChange" handler so that the
 627                          //    key-related handlers only get attached once.
 628  
 629                          handle.detach();
 630  
 631                      }
 632  
 633                  }, this));
 634  
 635                  aHandlers.push(handle);
 636  
 637              }
 638  
 639  
 640              this._eventHandlers = aHandlers;
 641  
 642          }
 643  
 644      },
 645  
 646  
 647      //    Protected event handlers
 648  
 649      /**
 650      * @method _onDocMouseDown
 651      * @description "mousedown" event handler for the owner document of the
 652      * Focus Manager's Node.
 653      * @protected
 654      * @param event {Object} Object representing the DOM event.
 655      */
 656      _onDocMouseDown: function (event) {
 657  
 658          var oHost = this.get(HOST),
 659              oTarget = event.target,
 660              bChildNode = oHost.contains(oTarget),
 661              node,
 662  
 663              getFocusable = function (node) {
 664  
 665                  var returnVal = false;
 666  
 667                  if (!node.compareTo(oHost)) {
 668  
 669                      returnVal = this._isDescendant(node) ? node :
 670                                      getFocusable.call(this, node.get("parentNode"));
 671  
 672                  }
 673  
 674                  return returnVal;
 675  
 676              };
 677  
 678  
 679          if (bChildNode) {
 680  
 681              //    Check to make sure that the target isn't a child node of one
 682              //    of the focusable descendants.
 683  
 684              node = getFocusable.call(this, oTarget);
 685  
 686              if (node) {
 687                  oTarget = node;
 688              }
 689              else if (!node && this.get(FOCUSED)) {
 690  
 691                  //    The target was a non-focusable descendant of the root
 692                  //    node, so the "focused" attribute should be set to false.
 693  
 694                   this._set(FOCUSED, false);
 695                   this._onDocFocus(event);
 696  
 697              }
 698  
 699          }
 700  
 701  
 702          if (bChildNode && this._isDescendant(oTarget)) {
 703  
 704              //    Fix general problem in Webkit: mousing down on a button or an
 705              //    anchor element doesn't focus it.
 706  
 707              //    For all browsers: makes sure that the descendant that
 708              //    was the target of the mousedown event is now considered the
 709              //    active descendant.
 710  
 711              this.focus(oTarget);
 712          }
 713          else if (UA.webkit && this.get(FOCUSED) &&
 714              (!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) {
 715  
 716              //    Fix for Webkit:
 717  
 718              //    Document doesn't receive focus in Webkit when the user mouses
 719              //    down on it, so the "focused" attribute won't get set to the
 720              //    correct value.
 721  
 722              //    The goal is to force a blur if the user moused down on
 723              //    either: 1) A descendant node, but not one that managed by
 724              //    the FocusManager, or 2) an element outside of the
 725              //    FocusManager
 726  
 727               this._set(FOCUSED, false);
 728               this._onDocFocus(event);
 729  
 730          }
 731  
 732      },
 733  
 734  
 735      /**
 736      * @method _onDocFocus
 737      * @description "focus" event handler for the owner document of the
 738      * Focus Manager's Node.
 739      * @protected
 740      * @param event {Object} Object representing the DOM event.
 741      */
 742      _onDocFocus: function (event) {
 743  
 744          var oTarget = this._focusTarget || event.target,
 745              bFocused = this.get(FOCUSED),
 746              focusClass = this.get(FOCUS_CLASS),
 747              oFocusedNode = this._focusedNode,
 748              bInCollection;
 749  
 750          if (this._focusTarget) {
 751              this._focusTarget = null;
 752          }
 753  
 754  
 755          if (this.get(HOST).contains(oTarget)) {
 756  
 757              //    The target is a descendant of the root Node.
 758  
 759              bInCollection = this._isDescendant(oTarget);
 760  
 761              if (!bFocused && bInCollection) {
 762  
 763                  //    The user has focused a focusable descendant.
 764  
 765                  bFocused = true;
 766  
 767              }
 768              else if (bFocused && !bInCollection) {
 769  
 770                  //    The user has focused a child of the root Node that is
 771                  //    not one of the descendants managed by this Focus Manager
 772                  //    so clear the currently focused descendant.
 773  
 774                  bFocused = false;
 775  
 776              }
 777  
 778          }
 779          else {
 780  
 781              // The target is some other node in the document.
 782  
 783              bFocused = false;
 784  
 785          }
 786  
 787  
 788          if (focusClass) {
 789  
 790              if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) {
 791                  this._removeFocusClass();
 792              }
 793  
 794              if (bInCollection && bFocused) {
 795  
 796                  if (focusClass.fn) {
 797                      oTarget = focusClass.fn(oTarget);
 798                      oTarget.addClass(focusClass.className);
 799                  }
 800                  else {
 801                      oTarget.addClass(focusClass);
 802                  }
 803  
 804                  this._focusedNode = oTarget;
 805  
 806              }
 807  
 808          }
 809  
 810  
 811          this._set(FOCUSED, bFocused);
 812  
 813      },
 814  
 815  
 816      /**
 817      * @method _focusNext
 818      * @description Keydown event handler that moves focus to the next
 819      * enabled descendant.
 820      * @protected
 821      * @param event {Object} Object representing the DOM event.
 822      * @param activeDescendant {Number} Number representing the index of the
 823      * next descendant to be focused
 824      */
 825      _focusNext: function (event, activeDescendant) {
 826  
 827          var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
 828              oNode;
 829  
 830  
 831          if (this._isDescendant(event.target) &&
 832              (nActiveDescendant <= this._lastNodeIndex)) {
 833  
 834              nActiveDescendant = nActiveDescendant + 1;
 835  
 836              if (nActiveDescendant === (this._lastNodeIndex + 1) &&
 837                  this.get(CIRCULAR)) {
 838  
 839                  nActiveDescendant = 0;
 840  
 841              }
 842  
 843              oNode = this._descendants.item(nActiveDescendant);
 844  
 845              if (oNode) {
 846  
 847                  if (oNode.get("disabled")) {
 848                      this._focusNext(event, nActiveDescendant);
 849                  }
 850                  else {
 851                      this.focus(nActiveDescendant);
 852                  }
 853  
 854              }
 855  
 856          }
 857  
 858          this._preventScroll(event);
 859  
 860      },
 861  
 862  
 863      /**
 864      * @method _focusPrevious
 865      * @description Keydown event handler that moves focus to the previous
 866      * enabled descendant.
 867      * @protected
 868      * @param event {Object} Object representing the DOM event.
 869      * @param activeDescendant {Number} Number representing the index of the
 870      * next descendant to be focused.
 871      */
 872      _focusPrevious: function (event, activeDescendant) {
 873  
 874          var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
 875              oNode;
 876  
 877          if (this._isDescendant(event.target) && nActiveDescendant >= 0) {
 878  
 879              nActiveDescendant = nActiveDescendant - 1;
 880  
 881              if (nActiveDescendant === -1 && this.get(CIRCULAR)) {
 882                  nActiveDescendant = this._lastNodeIndex;
 883              }
 884  
 885              oNode = this._descendants.item(nActiveDescendant);
 886  
 887              if (oNode) {
 888  
 889                  if (oNode.get("disabled")) {
 890                      this._focusPrevious(event, nActiveDescendant);
 891                  }
 892                  else {
 893                      this.focus(nActiveDescendant);
 894                  }
 895  
 896              }
 897  
 898          }
 899  
 900          this._preventScroll(event);
 901  
 902      },
 903  
 904  
 905      /**
 906      * @method _afterActiveDescendantChange
 907      * @description afterChange event handler for the
 908      * "activeDescendant" attribute.
 909      * @protected
 910      * @param event {Object} Object representing the change event.
 911      */
 912      _afterActiveDescendantChange: function (event) {
 913  
 914          var oNode = this._descendants.item(event.prevVal);
 915  
 916          if (oNode) {
 917              oNode.set(TAB_INDEX, -1);
 918          }
 919  
 920          oNode = this._descendants.item(event.newVal);
 921  
 922          if (oNode) {
 923              oNode.set(TAB_INDEX, 0);
 924          }
 925  
 926      },
 927  
 928  
 929  
 930      //    Public methods
 931  
 932      initializer: function (config) {
 933          Y.log("WARNING: node-focusmanager is a deprecated module as of YUI 3.9.0. This module will be removed from a later version of the library.", "warn");
 934          this.start();
 935  
 936      },
 937  
 938      destructor: function () {
 939  
 940          this.stop();
 941          this.get(HOST).focusManager = null;
 942  
 943      },
 944  
 945  
 946      /**
 947      * @method focus
 948      * @description Focuses the active descendant and sets the
 949      * <code>focused</code> attribute to true.
 950      * @param index {Number|Node} Optional. Number representing the index of the
 951      * descendant to be set as the active descendant or Node instance
 952      * representing the descendant to be set as the active descendant.
 953      */
 954      focus: function (index) {
 955  
 956          if (Lang.isUndefined(index)) {
 957              index = this.get(ACTIVE_DESCENDANT);
 958          }
 959  
 960          this.set(ACTIVE_DESCENDANT, index, { src: UI });
 961  
 962          var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
 963  
 964          if (oNode) {
 965  
 966              oNode.focus();
 967  
 968              //    In Opera focusing a <BUTTON> element programmatically
 969              //    will result in the document-level focus event handler
 970              //    "_onDocFocus" being called, resulting in the handler
 971              //    incorrectly setting the "focused" Attribute to false.  To fix
 972              //    this, set a flag ("_focusTarget") that the "_onDocFocus" method
 973              //    can look for to properly handle this edge case.
 974  
 975              if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") {
 976                  this._focusTarget = oNode;
 977              }
 978  
 979          }
 980  
 981      },
 982  
 983  
 984      /**
 985      * @method blur
 986      * @description Blurs the current active descendant and sets the
 987      * <code>focused</code> attribute to false.
 988      */
 989      blur: function () {
 990  
 991          var oNode;
 992  
 993          if (this.get(FOCUSED)) {
 994  
 995              oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
 996  
 997              if (oNode) {
 998  
 999                  oNode.blur();
1000  
1001                  //    For Opera and Webkit:  Blurring an element in either browser
1002                  //    doesn't result in another element (such as the document)
1003                  //    being focused.  Therefore, the "_onDocFocus" method
1004                  //    responsible for managing the application and removal of the
1005                  //    focus indicator class name is never called.
1006  
1007                  this._removeFocusClass();
1008  
1009              }
1010  
1011              this._set(FOCUSED, false, { src: UI });
1012          }
1013  
1014      },
1015  
1016  
1017      /**
1018      * @method start
1019      * @description Enables the Focus Manager.
1020      */
1021      start: function () {
1022  
1023          if (this._stopped) {
1024  
1025              this._initDescendants();
1026              this._attachEventHandlers();
1027  
1028              this._stopped = false;
1029  
1030          }
1031  
1032      },
1033  
1034  
1035      /**
1036      * @method stop
1037      * @description Disables the Focus Manager by detaching all event handlers.
1038      */
1039      stop: function () {
1040  
1041          if (!this._stopped) {
1042  
1043              this._detachEventHandlers();
1044  
1045              this._descendants = null;
1046              this._focusedNode = null;
1047              this._lastNodeIndex = 0;
1048              this._stopped = true;
1049  
1050          }
1051  
1052      },
1053  
1054  
1055      /**
1056      * @method refresh
1057      * @description Refreshes the Focus Manager's descendants by re-executing the
1058      * CSS selector query specified by the <code>descendants</code> attribute.
1059      */
1060      refresh: function () {
1061  
1062          this._initDescendants();
1063  
1064          if (!this._eventHandlers) {
1065              this._attachEventHandlers();
1066          }
1067  
1068      }
1069  
1070  });
1071  
1072  
1073  NodeFocusManager.NAME = "nodeFocusManager";
1074  NodeFocusManager.NS = "focusManager";
1075  
1076  Y.namespace("Plugin");
1077  Y.Plugin.NodeFocusManager = NodeFocusManager;
1078  
1079  
1080  }, '3.17.2', {"requires": ["attribute", "node", "plugin", "node-event-simulate", "event-key", "event-focus"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1