[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/node-menunav/ -> node-menunav-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-menunav', function (Y, NAME) {
   9  
  10  /**
  11  * <p>The MenuNav Node Plugin makes it easy to transform existing list-based
  12  * markup into traditional, drop down navigational menus that are both accessible
  13  * and easy to customize, and only require a small set of dependencies.</p>
  14  *
  15  *
  16  * <p>To use the MenuNav Node Plugin, simply pass a reference to the plugin to a
  17  * Node instance's <code>plug</code> method.</p>
  18  *
  19  * <p>
  20  * <code>
  21  * &#60;script type="text/javascript"&#62; <br>
  22  * <br>
  23  *         //    Call the "use" method, passing in "node-menunav".  This will <br>
  24  *         //    load the script and CSS for the MenuNav Node Plugin and all of <br>
  25  *         //    the required dependencies. <br>
  26  * <br>
  27  *         YUI().use("node-menunav", function(Y) { <br>
  28  * <br>
  29  *             //    Use the "contentready" event to initialize the menu when <br>
  30  *             //    the subtree of element representing the root menu <br>
  31  *             //    (&#60;div id="menu-1"&#62;) is ready to be scripted. <br>
  32  * <br>
  33  *             Y.on("contentready", function () { <br>
  34  * <br>
  35  *                 //    The scope of the callback will be a Node instance <br>
  36  *                 //    representing the root menu (&#60;div id="menu-1"&#62;). <br>
  37  *                 //    Therefore, since "this" represents a Node instance, it <br>
  38  *                 //    is possible to just call "this.plug" passing in a <br>
  39  *                //    reference to the MenuNav Node Plugin. <br>
  40  * <br>
  41  *                 this.plug(Y.Plugin.NodeMenuNav); <br>
  42  * <br>
  43  *             }, "#menu-1"); <br>
  44  * <br>
  45  *         }); <br>
  46  * <br>
  47  *     &#60;/script&#62; <br>
  48  * </code>
  49  * </p>
  50  *
  51  * <p>The MenuNav Node Plugin has several configuration properties that can be
  52  * set via an object literal that is passed as a second argument to a Node
  53  * instance's <code>plug</code> method.
  54  * </p>
  55  *
  56  * <p>
  57  * <code>
  58  * &#60;script type="text/javascript"&#62; <br>
  59  * <br>
  60  *         //    Call the "use" method, passing in "node-menunav".  This will <br>
  61  *         //    load the script and CSS for the MenuNav Node Plugin and all of <br>
  62  *         //    the required dependencies. <br>
  63  * <br>
  64  *         YUI().use("node-menunav", function(Y) { <br>
  65  * <br>
  66  *             //    Use the "contentready" event to initialize the menu when <br>
  67  *             //    the subtree of element representing the root menu <br>
  68  *             //    (&#60;div id="menu-1"&#62;) is ready to be scripted. <br>
  69  * <br>
  70  *             Y.on("contentready", function () { <br>
  71  * <br>
  72  *                 //    The scope of the callback will be a Node instance <br>
  73  *                 //    representing the root menu (&#60;div id="menu-1"&#62;). <br>
  74  *                 //    Therefore, since "this" represents a Node instance, it <br>
  75  *                 //    is possible to just call "this.plug" passing in a <br>
  76  *                //    reference to the MenuNav Node Plugin. <br>
  77  * <br>
  78  *                 this.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 });
  79  * <br><br>
  80  *             }, "#menu-1"); <br>
  81  * <br>
  82  *         }); <br>
  83  * <br>
  84  *     &#60;/script&#62; <br>
  85  * </code>
  86  * </p>
  87  *
  88  DEPRECATED. The MenuNav 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/>.
  89  
  90  @module node-menunav
  91  @deprecated 3.9.0
  92  */
  93  
  94  
  95      //    Util shortcuts
  96  
  97  var UA = Y.UA,
  98      later = Y.later,
  99      getClassName = Y.ClassNameManager.getClassName,
 100  
 101  
 102  
 103      //    Frequently used strings
 104  
 105      MENU = "menu",
 106      MENUITEM = "menuitem",
 107      HIDDEN = "hidden",
 108      PARENT_NODE = "parentNode",
 109      CHILDREN = "children",
 110      OFFSET_HEIGHT = "offsetHeight",
 111      OFFSET_WIDTH = "offsetWidth",
 112      PX = "px",
 113      ID = "id",
 114      PERIOD = ".",
 115      HANDLED_MOUSEOUT = "handledMouseOut",
 116      HANDLED_MOUSEOVER = "handledMouseOver",
 117      ACTIVE = "active",
 118      LABEL = "label",
 119      LOWERCASE_A = "a",
 120      MOUSEDOWN = "mousedown",
 121      KEYDOWN = "keydown",
 122      CLICK = "click",
 123      EMPTY_STRING = "",
 124      FIRST_OF_TYPE = "first-of-type",
 125      ROLE = "role",
 126      PRESENTATION = "presentation",
 127      DESCENDANTS = "descendants",
 128      UI = "UI",
 129      ACTIVE_DESCENDANT = "activeDescendant",
 130      USE_ARIA = "useARIA",
 131      ARIA_HIDDEN = "aria-hidden",
 132      CONTENT = "content",
 133      HOST = "host",
 134      ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
 135  
 136  
 137      //    Attribute keys
 138  
 139      AUTO_SUBMENU_DISPLAY = "autoSubmenuDisplay",
 140      MOUSEOUT_HIDE_DELAY = "mouseOutHideDelay",
 141  
 142  
 143      //    CSS class names
 144  
 145      CSS_MENU = getClassName(MENU),
 146      CSS_MENU_HIDDEN = getClassName(MENU, HIDDEN),
 147      CSS_MENU_HORIZONTAL = getClassName(MENU, "horizontal"),
 148      CSS_MENU_LABEL = getClassName(MENU, LABEL),
 149      CSS_MENU_LABEL_ACTIVE = getClassName(MENU, LABEL, ACTIVE),
 150      CSS_MENU_LABEL_MENUVISIBLE = getClassName(MENU, LABEL, (MENU + "visible")),
 151      CSS_MENUITEM = getClassName(MENUITEM),
 152      CSS_MENUITEM_ACTIVE = getClassName(MENUITEM, ACTIVE),
 153  
 154  
 155      //    CSS selectors
 156  
 157      MENU_SELECTOR = PERIOD + CSS_MENU,
 158      MENU_TOGGLE_SELECTOR = (PERIOD + getClassName(MENU, "toggle")),
 159      MENU_CONTENT_SELECTOR = PERIOD + getClassName(MENU, CONTENT),
 160      MENU_LABEL_SELECTOR = PERIOD + CSS_MENU_LABEL,
 161  
 162      STANDARD_QUERY = ">" + MENU_CONTENT_SELECTOR + ">ul>li>a",
 163      EXTENDED_QUERY = ">" + MENU_CONTENT_SELECTOR + ">ul>li>" + MENU_LABEL_SELECTOR + ">a:first-child";
 164  
 165  //    Utility functions
 166  
 167  
 168  var getPreviousSibling = function (node) {
 169  
 170      var oPrevious = node.previous(),
 171          oChildren;
 172  
 173      if (!oPrevious) {
 174          oChildren = node.get(PARENT_NODE).get(CHILDREN);
 175          oPrevious = oChildren.item(oChildren.size() - 1);
 176      }
 177  
 178  
 179      return oPrevious;
 180  
 181  };
 182  
 183  
 184  var getNextSibling = function (node) {
 185  
 186      var oNext = node.next();
 187  
 188      if (!oNext) {
 189          oNext = node.get(PARENT_NODE).get(CHILDREN).item(0);
 190      }
 191  
 192      return oNext;
 193  
 194  };
 195  
 196  
 197  var isAnchor = function (node) {
 198  
 199      var bReturnVal = false;
 200  
 201      if (node) {
 202          bReturnVal = node.get("nodeName").toLowerCase() === LOWERCASE_A;
 203      }
 204  
 205      return bReturnVal;
 206  
 207  };
 208  
 209  
 210  var isMenuItem = function (node) {
 211  
 212      return node.hasClass(CSS_MENUITEM);
 213  
 214  };
 215  
 216  
 217  var isMenuLabel = function (node) {
 218  
 219      return node.hasClass(CSS_MENU_LABEL);
 220  
 221  };
 222  
 223  
 224  var isHorizontalMenu = function (menu) {
 225  
 226      return menu.hasClass(CSS_MENU_HORIZONTAL);
 227  
 228  };
 229  
 230  
 231  var hasVisibleSubmenu = function (menuLabel) {
 232  
 233      return menuLabel.hasClass(CSS_MENU_LABEL_MENUVISIBLE);
 234  
 235  };
 236  
 237  
 238  var getItemAnchor = function (node) {
 239  
 240      return isAnchor(node) ? node : node.one(LOWERCASE_A);
 241  
 242  };
 243  
 244  
 245  var getNodeWithClass = function (node, className, searchAncestors) {
 246  
 247      var oItem;
 248  
 249      if (node) {
 250  
 251          if (node.hasClass(className)) {
 252              oItem = node;
 253          }
 254  
 255          if (!oItem && searchAncestors) {
 256              oItem = node.ancestor((PERIOD + className));
 257          }
 258  
 259      }
 260  
 261      return oItem;
 262  
 263  };
 264  
 265  
 266  var getParentMenu = function (node) {
 267  
 268      return node.ancestor(MENU_SELECTOR);
 269  
 270  };
 271  
 272  
 273  var getMenu = function (node, searchAncestors) {
 274  
 275      return getNodeWithClass(node, CSS_MENU, searchAncestors);
 276  
 277  };
 278  
 279  
 280  var getMenuItem = function (node, searchAncestors) {
 281  
 282      var oItem;
 283  
 284      if (node) {
 285          oItem = getNodeWithClass(node, CSS_MENUITEM, searchAncestors);
 286      }
 287  
 288      return oItem;
 289  
 290  };
 291  
 292  
 293  var getMenuLabel = function (node, searchAncestors) {
 294  
 295      var oItem;
 296  
 297      if (node) {
 298  
 299          if (searchAncestors) {
 300              oItem = getNodeWithClass(node, CSS_MENU_LABEL, searchAncestors);
 301          }
 302          else {
 303              oItem = getNodeWithClass(node, CSS_MENU_LABEL) ||
 304                  node.one((PERIOD + CSS_MENU_LABEL));
 305          }
 306  
 307      }
 308  
 309      return oItem;
 310  
 311  };
 312  
 313  
 314  var getItem = function (node, searchAncestors) {
 315  
 316      var oItem;
 317  
 318      if (node) {
 319          oItem = getMenuItem(node, searchAncestors) ||
 320              getMenuLabel(node, searchAncestors);
 321      }
 322  
 323      return oItem;
 324  
 325  };
 326  
 327  
 328  var getFirstItem = function (menu) {
 329  
 330      return getItem(menu.one("li"));
 331  
 332  };
 333  
 334  
 335  var getActiveClass = function (node) {
 336  
 337      return isMenuItem(node) ? CSS_MENUITEM_ACTIVE : CSS_MENU_LABEL_ACTIVE;
 338  
 339  };
 340  
 341  
 342  var handleMouseOverForNode = function (node, target) {
 343  
 344      return node && !node[HANDLED_MOUSEOVER] &&
 345          (node.compareTo(target) || node.contains(target));
 346  
 347  };
 348  
 349  
 350  var handleMouseOutForNode = function (node, relatedTarget) {
 351  
 352      return node && !node[HANDLED_MOUSEOUT] &&
 353          (!node.compareTo(relatedTarget) && !node.contains(relatedTarget));
 354  
 355  };
 356  
 357  /**
 358  * The NodeMenuNav class is a plugin for a Node instance.  The class is used via
 359  * the <a href="Node.html#method_plug"><code>plug</code></a> method of Node and
 360  * should not be instantiated directly.
 361  * @namespace plugin
 362  * @class NodeMenuNav
 363  */
 364  var NodeMenuNav = function () {
 365  
 366      NodeMenuNav.superclass.constructor.apply(this, arguments);
 367  
 368  };
 369  
 370  NodeMenuNav.NAME = "nodeMenuNav";
 371  NodeMenuNav.NS = "menuNav";
 372  
 373  
 374  /**
 375  * @property SHIM_TEMPLATE_TITLE
 376  * @description String representing the value for the <code>title</code>
 377  * attribute for the shim used to prevent <code>&#60;select&#62;</code> elements
 378  * from poking through menus in IE 6.
 379  * @default "Menu Stacking Shim"
 380  * @type String
 381  */
 382  NodeMenuNav.SHIM_TEMPLATE_TITLE = "Menu Stacking Shim";
 383  
 384  
 385  /**
 386  * @property SHIM_TEMPLATE
 387  * @description String representing the HTML used to create the
 388  * <code>&#60;iframe&#62;</code> shim used to prevent
 389  * <code>&#60;select&#62;</code> elements from poking through menus in IE 6.
 390  * @default &#34;&#60;iframe frameborder=&#34;0&#34; tabindex=&#34;-1&#34;
 391  * class=&#34;yui-shim&#34; title=&#34;Menu Stacking Shim&#34;
 392  * src=&#34;javascript:false;&#34;&#62;&#60;/iframe&#62;&#34;
 393  * @type String
 394  */
 395  
 396  //    <iframe> shim notes:
 397  //
 398  //    1) Need to set the "frameBorder" property to 0 to suppress the default
 399  //    <iframe> border in IE.  (Setting the CSS "border" property alone doesn't
 400  //    suppress it.)
 401  //
 402  //    2) The "src" attribute of the <iframe> is set to "javascript:false;" so
 403  //    that it won't load a page inside it, preventing the secure/nonsecure
 404  //    warning in IE when using HTTPS.
 405  //
 406  //    3) Since the role of the <iframe> shim is completely presentational, its
 407  //    "tabindex" attribute is set to "-1" and its title attribute is set to
 408  //    "Menu Stacking Shim".  Both strategies help users of screen readers to
 409  //    avoid mistakenly interacting with the <iframe> shim.
 410  
 411  NodeMenuNav.SHIM_TEMPLATE = '<iframe frameborder="0" tabindex="-1" class="' +
 412                              getClassName("shim") +
 413                              '" title="' + NodeMenuNav.SHIM_TEMPLATE_TITLE +
 414                              '" src="javascript:false;"></iframe>';
 415  
 416  
 417  NodeMenuNav.ATTRS = {
 418  
 419      /**
 420      * Boolean indicating if use of the WAI-ARIA Roles and States should be
 421      * enabled for the menu.
 422      *
 423      * @attribute useARIA
 424      * @readOnly
 425      * @writeOnce
 426      * @default true
 427      * @type boolean
 428      */
 429      useARIA: {
 430  
 431          value: true,
 432          writeOnce: true,
 433          lazyAdd: false,
 434          setter: function (value) {
 435  
 436              var oMenu = this.get(HOST),
 437                  oMenuLabel,
 438                  oMenuToggle,
 439                  oSubmenu,
 440                  sID;
 441  
 442              if (value) {
 443  
 444                  oMenu.set(ROLE, MENU);
 445  
 446                  oMenu.all("ul,li," + MENU_CONTENT_SELECTOR).set(ROLE, PRESENTATION);
 447  
 448                  oMenu.all((PERIOD + getClassName(MENUITEM, CONTENT))).set(ROLE, MENUITEM);
 449  
 450                  oMenu.all((PERIOD + CSS_MENU_LABEL)).each(function (node) {
 451  
 452                      oMenuLabel = node;
 453                      oMenuToggle = node.one(MENU_TOGGLE_SELECTOR);
 454  
 455                      if (oMenuToggle) {
 456                          oMenuToggle.set(ROLE, PRESENTATION);
 457                          oMenuLabel = oMenuToggle.previous();
 458                      }
 459  
 460                      oMenuLabel.set(ROLE, MENUITEM);
 461                      oMenuLabel.set("aria-haspopup", true);
 462  
 463                      oSubmenu = node.next();
 464  
 465                      if (oSubmenu) {
 466  
 467                          oSubmenu.set(ROLE, MENU);
 468  
 469                          oMenuLabel = oSubmenu.previous();
 470                          oMenuToggle = oMenuLabel.one(MENU_TOGGLE_SELECTOR);
 471  
 472                          if (oMenuToggle) {
 473                              oMenuLabel = oMenuToggle;
 474                          }
 475  
 476                          sID = Y.stamp(oMenuLabel);
 477  
 478                          if (!oMenuLabel.get(ID)) {
 479                              oMenuLabel.set(ID, sID);
 480                          }
 481  
 482                          oSubmenu.set("aria-labelledby", sID);
 483                          oSubmenu.set(ARIA_HIDDEN, true);
 484  
 485                      }
 486  
 487                  });
 488  
 489              }
 490  
 491          }
 492  
 493      },
 494  
 495  
 496      /**
 497      * Boolean indicating if submenus are automatically made visible when the
 498      * user mouses over the menu's items.
 499      *
 500      * @attribute autoSubmenuDisplay
 501      * @readOnly
 502      * @writeOnce
 503      * @default true
 504      * @type boolean
 505      */
 506      autoSubmenuDisplay: {
 507  
 508          value: true,
 509          writeOnce: true
 510  
 511      },
 512  
 513  
 514      /**
 515      * Number indicating the time (in milliseconds) that should expire before a
 516      * submenu is made visible when the user mouses over the menu's label.
 517      *
 518      * @attribute submenuShowDelay
 519      * @readOnly
 520      * @writeOnce
 521      * @default 250
 522      * @type Number
 523      */
 524      submenuShowDelay: {
 525  
 526          value: 250,
 527          writeOnce: true
 528  
 529      },
 530  
 531  
 532      /**
 533      * Number indicating the time (in milliseconds) that should expire before a
 534      * submenu is hidden when the user mouses out of a menu label heading in the
 535      * direction of a submenu.
 536      *
 537      * @attribute submenuHideDelay
 538      * @readOnly
 539      * @writeOnce
 540      * @default 250
 541      * @type Number
 542      */
 543      submenuHideDelay: {
 544  
 545          value: 250,
 546          writeOnce: true
 547  
 548      },
 549  
 550  
 551      /**
 552      * Number indicating the time (in milliseconds) that should expire before a
 553      * submenu is hidden when the user mouses out of it.
 554      *
 555      * @attribute mouseOutHideDelay
 556      * @readOnly
 557      * @writeOnce
 558      * @default 750
 559      * @type Number
 560      */
 561      mouseOutHideDelay: {
 562  
 563          value: 750,
 564          writeOnce: true
 565  
 566      }
 567  
 568  };
 569  
 570  
 571  Y.extend(NodeMenuNav, Y.Plugin.Base, {
 572  
 573      //    Protected properties
 574  
 575      /**
 576      * @property _rootMenu
 577      * @description Node instance representing the root menu in the menu.
 578      * @default null
 579      * @protected
 580      * @type Node
 581      */
 582      _rootMenu: null,
 583  
 584  
 585      /**
 586      * @property _activeItem
 587      * @description Node instance representing the menu's active descendent:
 588      * the menuitem or menu label the user is currently interacting with.
 589      * @default null
 590      * @protected
 591      * @type Node
 592      */
 593      _activeItem: null,
 594  
 595  
 596      /**
 597      * @property _activeMenu
 598      * @description Node instance representing the menu that is the parent of
 599      * the menu's active descendent.
 600      * @default null
 601      * @protected
 602      * @type Node
 603      */
 604      _activeMenu: null,
 605  
 606  
 607      /**
 608      * @property _hasFocus
 609      * @description Boolean indicating if the menu has focus.
 610      * @default false
 611      * @protected
 612      * @type Boolean
 613      */
 614      _hasFocus: false,
 615  
 616  
 617      //    In gecko-based browsers a mouseover and mouseout event will fire even
 618      //    if a DOM element moves out from under the mouse without the user
 619      //    actually moving the mouse.  This bug affects NodeMenuNav because the
 620      //    user can hit the Esc key to hide a menu, and if the mouse is over the
 621      //    menu when the user presses Esc, the _onMenuMouseOut handler will be
 622      //    called.  To fix this bug the following flag (_blockMouseEvent) is used
 623      // to block the code in the _onMenuMouseOut handler from executing.
 624  
 625      /**
 626      * @property _blockMouseEvent
 627      * @description Boolean indicating whether or not to handle the
 628      * "mouseover" event.
 629      * @default false
 630      * @protected
 631      * @type Boolean
 632      */
 633      _blockMouseEvent: false,
 634  
 635  
 636      /**
 637      * @property _currentMouseX
 638      * @description Number representing the current x coordinate of the mouse
 639      * inside the menu.
 640      * @default 0
 641      * @protected
 642      * @type Number
 643      */
 644      _currentMouseX: 0,
 645  
 646  
 647      /**
 648      * @property _movingToSubmenu
 649      * @description Boolean indicating if the mouse is moving from a menu
 650      * label to its corresponding submenu.
 651      * @default false
 652      * @protected
 653      * @type Boolean
 654      */
 655      _movingToSubmenu: false,
 656  
 657  
 658      /**
 659      * @property _showSubmenuTimer
 660      * @description Timer used to show a submenu.
 661      * @default null
 662      * @protected
 663      * @type Object
 664      */
 665      _showSubmenuTimer: null,
 666  
 667  
 668      /**
 669      * @property _hideSubmenuTimer
 670      * @description Timer used to hide a submenu.
 671      * @default null
 672      * @protected
 673      * @type Object
 674      */
 675      _hideSubmenuTimer: null,
 676  
 677  
 678      /**
 679      * @property _hideAllSubmenusTimer
 680      * @description Timer used to hide a all submenus.
 681      * @default null
 682      * @protected
 683      * @type Object
 684      */
 685      _hideAllSubmenusTimer: null,
 686  
 687  
 688      /**
 689      * @property _firstItem
 690      * @description Node instance representing the first item (menuitem or menu
 691      * label) in the root menu of a menu.
 692      * @default null
 693      * @protected
 694      * @type Node
 695      */
 696      _firstItem: null,
 697  
 698  
 699      //    Public methods
 700  
 701  
 702      initializer: function (config) {
 703  
 704          var menuNav = this,
 705              oRootMenu = this.get(HOST),
 706              aHandlers = [],
 707              oDoc;
 708  
 709          Y.log("WARNING: Node-MenuNav is a deprecated module as of YUI 3.9.0. This module will be removed from a later version of the library.", "warn");
 710  
 711          if (oRootMenu) {
 712  
 713              menuNav._rootMenu = oRootMenu;
 714  
 715              oRootMenu.all("ul:first-child").addClass(FIRST_OF_TYPE);
 716  
 717              //    Hide all visible submenus
 718  
 719              oRootMenu.all(MENU_SELECTOR).addClass(CSS_MENU_HIDDEN);
 720  
 721  
 722              //    Wire up all event handlers
 723  
 724              aHandlers.push(oRootMenu.on("mouseover", menuNav._onMouseOver, menuNav));
 725              aHandlers.push(oRootMenu.on("mouseout", menuNav._onMouseOut, menuNav));
 726              aHandlers.push(oRootMenu.on("mousemove", menuNav._onMouseMove, menuNav));
 727              aHandlers.push(oRootMenu.on(MOUSEDOWN, menuNav._toggleSubmenuDisplay, menuNav));
 728              aHandlers.push(Y.on("key", menuNav._toggleSubmenuDisplay, oRootMenu, "down:13", menuNav));
 729              aHandlers.push(oRootMenu.on(CLICK, menuNav._toggleSubmenuDisplay, menuNav));
 730              aHandlers.push(oRootMenu.on("keypress", menuNav._onKeyPress, menuNav));
 731              aHandlers.push(oRootMenu.on(KEYDOWN, menuNav._onKeyDown, menuNav));
 732  
 733              oDoc = oRootMenu.get("ownerDocument");
 734  
 735              aHandlers.push(oDoc.on(MOUSEDOWN, menuNav._onDocMouseDown, menuNav));
 736              aHandlers.push(oDoc.on("focus", menuNav._onDocFocus, menuNav));
 737  
 738              this._eventHandlers = aHandlers;
 739  
 740              menuNav._initFocusManager();
 741  
 742          }
 743  
 744  
 745      },
 746  
 747      destructor: function () {
 748  
 749          var aHandlers = this._eventHandlers;
 750  
 751          if (aHandlers) {
 752  
 753              Y.Array.each(aHandlers, function (handle) {
 754                  handle.detach();
 755              });
 756  
 757              this._eventHandlers = null;
 758  
 759          }
 760  
 761          this.get(HOST).unplug("focusManager");
 762  
 763      },
 764  
 765  
 766  
 767      //    Protected methods
 768  
 769      /**
 770      * @method _isRoot
 771      * @description Returns a boolean indicating if the specified menu is the
 772      * root menu in the menu.
 773      * @protected
 774      * @param {Node} menu Node instance representing a menu.
 775      * @return {Boolean} Boolean indicating if the specified menu is the root
 776      * menu in the menu.
 777      */
 778      _isRoot: function (menu) {
 779  
 780          return this._rootMenu.compareTo(menu);
 781  
 782      },
 783  
 784  
 785      /**
 786      * @method _getTopmostSubmenu
 787      * @description Returns the topmost submenu of a submenu hierarchy.
 788      * @protected
 789      * @param {Node} menu Node instance representing a menu.
 790      * @return {Node} Node instance representing a menu.
 791      */
 792      _getTopmostSubmenu: function (menu) {
 793  
 794          var menuNav = this,
 795              oMenu = getParentMenu(menu),
 796              returnVal;
 797  
 798  
 799          if (!oMenu) {
 800              returnVal = menu;
 801          }
 802          else if (menuNav._isRoot(oMenu)) {
 803              returnVal = menu;
 804          }
 805          else {
 806              returnVal = menuNav._getTopmostSubmenu(oMenu);
 807          }
 808  
 809          return returnVal;
 810  
 811      },
 812  
 813  
 814      /**
 815      * @method _clearActiveItem
 816      * @description Clears the menu's active descendent.
 817      * @protected
 818      */
 819      _clearActiveItem: function () {
 820  
 821          var menuNav = this,
 822              oActiveItem = menuNav._activeItem;
 823  
 824          if (oActiveItem) {
 825              oActiveItem.removeClass(getActiveClass(oActiveItem));
 826          }
 827  
 828          menuNav._activeItem = null;
 829  
 830      },
 831  
 832  
 833      /**
 834      * @method _setActiveItem
 835      * @description Sets the specified menuitem or menu label as the menu's
 836      * active descendent.
 837      * @protected
 838      * @param {Node} item Node instance representing a menuitem or menu label.
 839      */
 840      _setActiveItem: function (item) {
 841  
 842          var menuNav = this;
 843  
 844          if (item) {
 845  
 846              menuNav._clearActiveItem();
 847  
 848              item.addClass(getActiveClass(item));
 849  
 850              menuNav._activeItem = item;
 851  
 852          }
 853  
 854      },
 855  
 856  
 857      /**
 858      * @method _focusItem
 859      * @description Focuses the specified menuitem or menu label.
 860      * @protected
 861      * @param {Node} item Node instance representing a menuitem or menu label.
 862      */
 863      _focusItem: function (item) {
 864  
 865          var menuNav = this,
 866              oMenu,
 867              oItem;
 868  
 869          if (item && menuNav._hasFocus) {
 870  
 871              oMenu = getParentMenu(item);
 872              oItem = getItemAnchor(item);
 873  
 874              if (oMenu && !oMenu.compareTo(menuNav._activeMenu)) {
 875                  menuNav._activeMenu = oMenu;
 876                  menuNav._initFocusManager();
 877              }
 878  
 879              menuNav._focusManager.focus(oItem);
 880  
 881          }
 882  
 883      },
 884  
 885  
 886      /**
 887      * @method _showMenu
 888      * @description Shows the specified menu.
 889      * @protected
 890      * @param {Node} menu Node instance representing a menu.
 891      */
 892      _showMenu: function (menu) {
 893  
 894          var oParentMenu = getParentMenu(menu),
 895              oLI = menu.get(PARENT_NODE),
 896              aXY = oLI.getXY();
 897  
 898  
 899          if (this.get(USE_ARIA)) {
 900              menu.set(ARIA_HIDDEN, false);
 901          }
 902  
 903  
 904          if (isHorizontalMenu(oParentMenu)) {
 905              aXY[1] = aXY[1] + oLI.get(OFFSET_HEIGHT);
 906          }
 907          else {
 908              aXY[0] = aXY[0] + oLI.get(OFFSET_WIDTH);
 909          }
 910  
 911          menu.setXY(aXY);
 912  
 913          if (UA.ie && UA.ie < 8) {
 914  
 915              if (UA.ie === 6 && !menu.hasIFrameShim) {
 916  
 917                  menu.appendChild(Y.Node.create(NodeMenuNav.SHIM_TEMPLATE));
 918                  menu.hasIFrameShim = true;
 919  
 920              }
 921  
 922              //    Clear previous values for height and width
 923  
 924              menu.setStyles({ height: EMPTY_STRING, width: EMPTY_STRING });
 925  
 926              //    Set the width and height of the menu's bounding box - this is
 927              //    necessary for IE 6 so that the CSS for the <iframe> shim can
 928              //    simply set the <iframe>'s width and height to 100% to ensure
 929              //    that dimensions of an <iframe> shim are always sync'd to the
 930              //    that of its parent menu.  Specifying a width and height also
 931              //    helps when positioning decorator elements (for creating effects
 932              //    like rounded corners) inside a menu's bounding box in IE 7.
 933  
 934              menu.setStyles({
 935                  height: (menu.get(OFFSET_HEIGHT) + PX),
 936                  width: (menu.get(OFFSET_WIDTH) + PX) });
 937  
 938          }
 939  
 940          menu.previous().addClass(CSS_MENU_LABEL_MENUVISIBLE);
 941          menu.removeClass(CSS_MENU_HIDDEN);
 942  
 943      },
 944  
 945  
 946      /**
 947      * @method _hideMenu
 948      * @description Hides the specified menu.
 949      * @protected
 950      * @param {Node} menu Node instance representing a menu.
 951      * @param {Boolean} activateAndFocusLabel Boolean indicating if the label
 952      * for the specified
 953      * menu should be focused and set as active.
 954      */
 955      _hideMenu: function (menu, activateAndFocusLabel) {
 956  
 957          var menuNav = this,
 958              oLabel = menu.previous(),
 959              oActiveItem;
 960  
 961          oLabel.removeClass(CSS_MENU_LABEL_MENUVISIBLE);
 962  
 963  
 964          if (activateAndFocusLabel) {
 965              menuNav._focusItem(oLabel);
 966              menuNav._setActiveItem(oLabel);
 967          }
 968  
 969          oActiveItem = menu.one((PERIOD + CSS_MENUITEM_ACTIVE));
 970  
 971          if (oActiveItem) {
 972              oActiveItem.removeClass(CSS_MENUITEM_ACTIVE);
 973          }
 974  
 975          //    Clear the values for top and left that were set by the call to
 976          //    "setXY" when the menu was shown so that the hidden position
 977          //    specified in the core CSS file will take affect.
 978  
 979          menu.setStyles({ left: EMPTY_STRING, top: EMPTY_STRING });
 980  
 981          menu.addClass(CSS_MENU_HIDDEN);
 982  
 983          if (menuNav.get(USE_ARIA)) {
 984              menu.set(ARIA_HIDDEN, true);
 985          }
 986  
 987      },
 988  
 989  
 990      /**
 991      * @method _hideAllSubmenus
 992      * @description Hides all submenus of the specified menu.
 993      * @protected
 994      * @param {Node} menu Node instance representing a menu.
 995      */
 996      _hideAllSubmenus: function (menu) {
 997  
 998          var menuNav = this;
 999  
1000          menu.all(MENU_SELECTOR).each(Y.bind(function (submenuNode) {
1001  
1002              menuNav._hideMenu(submenuNode);
1003  
1004          }, menuNav));
1005  
1006      },
1007  
1008  
1009      /**
1010      * @method _cancelShowSubmenuTimer
1011      * @description Cancels the timer used to show a submenu.
1012      * @protected
1013      */
1014      _cancelShowSubmenuTimer: function () {
1015  
1016          var menuNav = this,
1017              oShowSubmenuTimer = menuNav._showSubmenuTimer;
1018  
1019          if (oShowSubmenuTimer) {
1020              oShowSubmenuTimer.cancel();
1021              menuNav._showSubmenuTimer = null;
1022          }
1023  
1024      },
1025  
1026  
1027      /**
1028      * @method _cancelHideSubmenuTimer
1029      * @description Cancels the timer used to hide a submenu.
1030      * @protected
1031      */
1032      _cancelHideSubmenuTimer: function () {
1033  
1034          var menuNav = this,
1035              oHideSubmenuTimer = menuNav._hideSubmenuTimer;
1036  
1037  
1038          if (oHideSubmenuTimer) {
1039              oHideSubmenuTimer.cancel();
1040              menuNav._hideSubmenuTimer = null;
1041          }
1042  
1043      },
1044  
1045  
1046      /**
1047      * @method _initFocusManager
1048      * @description Initializes and updates the Focus Manager so that is is
1049      * always managing descendants of the active menu.
1050      * @protected
1051      */
1052      _initFocusManager: function () {
1053  
1054          var menuNav = this,
1055              oRootMenu = menuNav._rootMenu,
1056              oMenu = menuNav._activeMenu || oRootMenu,
1057              sSelectorBase =
1058                  menuNav._isRoot(oMenu) ? EMPTY_STRING : ("#" + oMenu.get("id")),
1059              oFocusManager = menuNav._focusManager,
1060              sKeysVal,
1061              sDescendantSelector,
1062              sQuery;
1063  
1064          if (isHorizontalMenu(oMenu)) {
1065  
1066              sDescendantSelector = sSelectorBase + STANDARD_QUERY + "," +
1067                  sSelectorBase + EXTENDED_QUERY;
1068  
1069              sKeysVal = { next: "down:39", previous: "down:37" };
1070  
1071          }
1072          else {
1073  
1074              sDescendantSelector = sSelectorBase + STANDARD_QUERY;
1075              sKeysVal = { next: "down:40", previous: "down:38" };
1076  
1077          }
1078  
1079  
1080          if (!oFocusManager) {
1081  
1082              oRootMenu.plug(Y.Plugin.NodeFocusManager, {
1083                  descendants: sDescendantSelector,
1084                  keys: sKeysVal,
1085                  circular: true
1086              });
1087  
1088              oFocusManager = oRootMenu.focusManager;
1089  
1090              sQuery = "#" + oRootMenu.get("id") + MENU_SELECTOR + " a," +
1091                              MENU_TOGGLE_SELECTOR;
1092  
1093              oRootMenu.all(sQuery).set("tabIndex", -1);
1094  
1095              oFocusManager.on(ACTIVE_DESCENDANT_CHANGE,
1096                  this._onActiveDescendantChange, oFocusManager, this);
1097  
1098              oFocusManager.after(ACTIVE_DESCENDANT_CHANGE,
1099                  this._afterActiveDescendantChange, oFocusManager, this);
1100  
1101              menuNav._focusManager = oFocusManager;
1102  
1103          }
1104          else {
1105  
1106              oFocusManager.set(ACTIVE_DESCENDANT, -1);
1107              oFocusManager.set(DESCENDANTS, sDescendantSelector);
1108              oFocusManager.set("keys", sKeysVal);
1109  
1110          }
1111  
1112      },
1113  
1114  
1115      //    Event handlers for discrete pieces of pieces of the menu
1116  
1117  
1118      /**
1119      * @method _onActiveDescendantChange
1120      * @description "activeDescendantChange" event handler for menu's
1121      * Focus Manager.
1122      * @protected
1123      * @param {Object} event Object representing the Attribute change event.
1124      * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
1125      */
1126      _onActiveDescendantChange: function (event, menuNav) {
1127  
1128          if (event.src === UI && menuNav._activeMenu &&
1129                  !menuNav._movingToSubmenu) {
1130  
1131              menuNav._hideAllSubmenus(menuNav._activeMenu);
1132  
1133          }
1134  
1135      },
1136  
1137  
1138      /**
1139      * @method _afterActiveDescendantChange
1140      * @description "activeDescendantChange" event handler for menu's
1141      * Focus Manager.
1142      * @protected
1143      * @param {Object} event Object representing the Attribute change event.
1144      * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
1145      */
1146      _afterActiveDescendantChange: function (event, menuNav) {
1147  
1148          var oItem;
1149  
1150          if (event.src === UI) {
1151              oItem = getItem(this.get(DESCENDANTS).item(event.newVal), true);
1152              menuNav._setActiveItem(oItem);
1153          }
1154  
1155      },
1156  
1157  
1158      /**
1159      * @method _onDocFocus
1160      * @description "focus" event handler for the owner document of the MenuNav.
1161      * @protected
1162      * @param {Object} event Object representing the DOM event.
1163      */
1164      _onDocFocus: function (event) {
1165  
1166          var menuNav = this,
1167              oActiveItem = menuNav._activeItem,
1168              oTarget = event.target,
1169              oMenu;
1170  
1171  
1172          if (menuNav._rootMenu.contains(oTarget)) {    //    The menu has focus
1173  
1174              if (menuNav._hasFocus) {
1175  
1176                  oMenu = getParentMenu(oTarget);
1177  
1178                  //    If the element that was focused is a descendant of the
1179                  //    root menu, but is in a submenu not currently being
1180                  //    managed by the Focus Manager, update the Focus Manager so
1181                  //    that it is now managing the submenu that is the parent of
1182                  //    the element that was focused.
1183  
1184                  if (!menuNav._activeMenu.compareTo(oMenu)) {
1185  
1186                      menuNav._activeMenu = oMenu;
1187                      menuNav._initFocusManager();
1188                      menuNav._focusManager.set(ACTIVE_DESCENDANT, oTarget);
1189                      menuNav._setActiveItem(getItem(oTarget, true));
1190  
1191                  }
1192  
1193              }
1194              else { //    Initial focus
1195  
1196                  //    First time the menu has been focused, need to setup focused
1197                  //    state and established active active descendant
1198  
1199                  menuNav._hasFocus = true;
1200  
1201                  oActiveItem = getItem(oTarget, true);
1202  
1203                  if (oActiveItem) {
1204                      menuNav._setActiveItem(oActiveItem);
1205                  }
1206  
1207              }
1208  
1209          }
1210          else {    //    The menu has lost focus
1211  
1212              menuNav._clearActiveItem();
1213  
1214              menuNav._cancelShowSubmenuTimer();
1215              menuNav._hideAllSubmenus(menuNav._rootMenu);
1216  
1217              menuNav._activeMenu = menuNav._rootMenu;
1218              menuNav._initFocusManager();
1219  
1220              menuNav._focusManager.set(ACTIVE_DESCENDANT, 0);
1221  
1222              menuNav._hasFocus = false;
1223  
1224          }
1225  
1226      },
1227  
1228  
1229      /**
1230      * @method _onMenuMouseOver
1231      * @description "mouseover" event handler for a menu.
1232      * @protected
1233      * @param {Node} menu Node instance representing a menu.
1234      * @param {Object} event Object representing the DOM event.
1235      */
1236      _onMenuMouseOver: function (menu, event) {
1237  
1238          var menuNav = this,
1239              oHideAllSubmenusTimer = menuNav._hideAllSubmenusTimer;
1240  
1241          if (oHideAllSubmenusTimer) {
1242              oHideAllSubmenusTimer.cancel();
1243              menuNav._hideAllSubmenusTimer = null;
1244          }
1245  
1246          menuNav._cancelHideSubmenuTimer();
1247  
1248          //    Need to update the FocusManager in advance of focus a new
1249          //    Menu in order to avoid the FocusManager thinking that
1250          //    it has lost focus
1251  
1252          if (menu && !menu.compareTo(menuNav._activeMenu)) {
1253              menuNav._activeMenu = menu;
1254  
1255              if (menuNav._hasFocus) {
1256                  menuNav._initFocusManager();
1257              }
1258  
1259          }
1260  
1261          if (menuNav._movingToSubmenu && isHorizontalMenu(menu)) {
1262              menuNav._movingToSubmenu = false;
1263          }
1264  
1265      },
1266  
1267  
1268      /**
1269      * @method _hideAndFocusLabel
1270      * @description Hides all of the submenus of the root menu and focuses the
1271      * label of the topmost submenu
1272      * @protected
1273      */
1274      _hideAndFocusLabel: function () {
1275  
1276          var    menuNav = this,
1277              oActiveMenu = menuNav._activeMenu,
1278              oSubmenu;
1279  
1280          menuNav._hideAllSubmenus(menuNav._rootMenu);
1281  
1282          if (oActiveMenu) {
1283  
1284              //    Focus the label element for the topmost submenu
1285              oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
1286              menuNav._focusItem(oSubmenu.previous());
1287  
1288          }
1289  
1290      },
1291  
1292  
1293      /**
1294      * @method _onMenuMouseOut
1295      * @description "mouseout" event handler for a menu.
1296      * @protected
1297      * @param {Node} menu Node instance representing a menu.
1298      * @param {Object} event Object representing the DOM event.
1299      */
1300      _onMenuMouseOut: function (menu, event) {
1301  
1302          var menuNav = this,
1303              oActiveMenu = menuNav._activeMenu,
1304              oRelatedTarget = event.relatedTarget,
1305              oActiveItem = menuNav._activeItem,
1306              oParentMenu,
1307              oMenu;
1308  
1309  
1310          if (oActiveMenu && !oActiveMenu.contains(oRelatedTarget)) {
1311  
1312              oParentMenu = getParentMenu(oActiveMenu);
1313  
1314  
1315              if (oParentMenu && !oParentMenu.contains(oRelatedTarget)) {
1316  
1317                  if (menuNav.get(MOUSEOUT_HIDE_DELAY) > 0) {
1318  
1319                      menuNav._cancelShowSubmenuTimer();
1320  
1321                      menuNav._hideAllSubmenusTimer =
1322  
1323                          later(menuNav.get(MOUSEOUT_HIDE_DELAY),
1324                              menuNav, menuNav._hideAndFocusLabel);
1325  
1326                  }
1327  
1328              }
1329              else {
1330  
1331                  if (oActiveItem) {
1332  
1333                      oMenu = getParentMenu(oActiveItem);
1334  
1335                      if (!menuNav._isRoot(oMenu)) {
1336                          menuNav._focusItem(oMenu.previous());
1337                      }
1338  
1339                  }
1340  
1341              }
1342  
1343          }
1344  
1345      },
1346  
1347  
1348      /**
1349      * @method _onMenuLabelMouseOver
1350      * @description "mouseover" event handler for a menu label.
1351      * @protected
1352      * @param {Node} menuLabel Node instance representing a menu label.
1353      * @param {Object} event Object representing the DOM event.
1354      */
1355      _onMenuLabelMouseOver: function (menuLabel, event) {
1356  
1357          var menuNav = this,
1358              oActiveMenu = menuNav._activeMenu,
1359              bIsRoot = menuNav._isRoot(oActiveMenu),
1360              bUseAutoSubmenuDisplay =
1361                  (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
1362              submenuShowDelay = menuNav.get("submenuShowDelay"),
1363              oSubmenu;
1364  
1365  
1366          var showSubmenu = function (delay) {
1367  
1368              menuNav._cancelHideSubmenuTimer();
1369              menuNav._cancelShowSubmenuTimer();
1370  
1371              if (!hasVisibleSubmenu(menuLabel)) {
1372  
1373                  oSubmenu = menuLabel.next();
1374  
1375                  if (oSubmenu) {
1376                      menuNav._hideAllSubmenus(oActiveMenu);
1377                      menuNav._showSubmenuTimer = later(delay, menuNav, menuNav._showMenu, oSubmenu);
1378                  }
1379  
1380              }
1381  
1382          };
1383  
1384  
1385          menuNav._focusItem(menuLabel);
1386          menuNav._setActiveItem(menuLabel);
1387  
1388  
1389          if (bUseAutoSubmenuDisplay) {
1390  
1391              if (menuNav._movingToSubmenu) {
1392  
1393                  //  If the user is moving diagonally from a submenu to
1394                  //  another submenu and they then stop and pause on a
1395                  //  menu label for an amount of time equal to the amount of
1396                  //  time defined for the display of a submenu then show the
1397                  //  submenu immediately.
1398                  //  http://yuilibrary.com/projects/yui3/ticket/2528316
1399  
1400                  //Y.message("Pause path");
1401  
1402                  menuNav._hoverTimer = later(submenuShowDelay, menuNav, function () {
1403                      showSubmenu(0);
1404                  });
1405  
1406              }
1407              else {
1408                  showSubmenu(submenuShowDelay);
1409              }
1410  
1411          }
1412  
1413      },
1414  
1415  
1416      /**
1417      * @method _onMenuLabelMouseOut
1418      * @description "mouseout" event handler for a menu label.
1419      * @protected
1420      * @param {Node} menuLabel Node instance representing a menu label.
1421      * @param {Object} event Object representing the DOM event.
1422      */
1423      _onMenuLabelMouseOut: function (menuLabel, event) {
1424  
1425          var menuNav = this,
1426              bIsRoot = menuNav._isRoot(menuNav._activeMenu),
1427              bUseAutoSubmenuDisplay =
1428                  (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
1429  
1430              oRelatedTarget = event.relatedTarget,
1431              oSubmenu = menuLabel.next(),
1432              hoverTimer = menuNav._hoverTimer;
1433  
1434          if (hoverTimer) {
1435              hoverTimer.cancel();
1436          }
1437  
1438          menuNav._clearActiveItem();
1439  
1440          if (bUseAutoSubmenuDisplay) {
1441  
1442              if (menuNav._movingToSubmenu &&
1443                      !menuNav._showSubmenuTimer && oSubmenu) {
1444  
1445                  //    If the mouse is moving diagonally toward the submenu and
1446                  //    another submenu isn't in the process of being displayed
1447                  //    (via a timer), then hide the submenu via a timer to give
1448                  //    the user some time to reach the submenu.
1449  
1450                  menuNav._hideSubmenuTimer =
1451                      later(menuNav.get("submenuHideDelay"), menuNav,
1452                          menuNav._hideMenu, oSubmenu);
1453  
1454              }
1455              else if (!menuNav._movingToSubmenu && oSubmenu && (!oRelatedTarget ||
1456                      (oRelatedTarget &&
1457                          !oSubmenu.contains(oRelatedTarget) &&
1458                          !oRelatedTarget.compareTo(oSubmenu)))) {
1459  
1460                  //    If the mouse is not moving toward the submenu, cancel any
1461                  //    submenus that might be in the process of being displayed
1462                  //    (via a timer) and hide this submenu immediately.
1463  
1464                  menuNav._cancelShowSubmenuTimer();
1465  
1466                  menuNav._hideMenu(oSubmenu);
1467  
1468              }
1469  
1470          }
1471  
1472      },
1473  
1474  
1475      /**
1476      * @method _onMenuItemMouseOver
1477      * @description "mouseover" event handler for a menuitem.
1478      * @protected
1479      * @param {Node} menuItem Node instance representing a menuitem.
1480      * @param {Object} event Object representing the DOM event.
1481      */
1482      _onMenuItemMouseOver: function (menuItem, event) {
1483  
1484          var menuNav = this,
1485              oActiveMenu = menuNav._activeMenu,
1486              bIsRoot = menuNav._isRoot(oActiveMenu),
1487              bUseAutoSubmenuDisplay =
1488                  (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot);
1489  
1490  
1491          menuNav._focusItem(menuItem);
1492          menuNav._setActiveItem(menuItem);
1493  
1494  
1495          if (bUseAutoSubmenuDisplay && !menuNav._movingToSubmenu) {
1496  
1497              menuNav._hideAllSubmenus(oActiveMenu);
1498  
1499          }
1500  
1501      },
1502  
1503  
1504      /**
1505      * @method _onMenuItemMouseOut
1506      * @description "mouseout" event handler for a menuitem.
1507      * @protected
1508      * @param {Node} menuItem Node instance representing a menuitem.
1509      * @param {Object} event Object representing the DOM event.
1510      */
1511      _onMenuItemMouseOut: function (menuItem, event) {
1512  
1513          this._clearActiveItem();
1514  
1515      },
1516  
1517  
1518      /**
1519      * @method _onVerticalMenuKeyDown
1520      * @description "keydown" event handler for vertical menus.
1521      * @protected
1522      * @param {Object} event Object representing the DOM event.
1523      */
1524      _onVerticalMenuKeyDown: function (event) {
1525  
1526          var menuNav = this,
1527              oActiveMenu = menuNav._activeMenu,
1528              oRootMenu = menuNav._rootMenu,
1529              oTarget = event.target,
1530              bPreventDefault = false,
1531              nKeyCode = event.keyCode,
1532              oSubmenu,
1533              oParentMenu,
1534              oLI,
1535              oItem;
1536  
1537  
1538          switch (nKeyCode) {
1539  
1540              case 37:    //    left arrow
1541  
1542                  oParentMenu = getParentMenu(oActiveMenu);
1543  
1544                  if (oParentMenu && isHorizontalMenu(oParentMenu)) {
1545  
1546                      menuNav._hideMenu(oActiveMenu);
1547                      oLI = getPreviousSibling(oActiveMenu.get(PARENT_NODE));
1548                      oItem = getItem(oLI);
1549  
1550                      if (oItem) {
1551  
1552                          if (isMenuLabel(oItem)) {    //    Menu label
1553  
1554                              oSubmenu = oItem.next();
1555  
1556  
1557                              if (oSubmenu) {
1558  
1559                                  menuNav._showMenu(oSubmenu);
1560                                  menuNav._focusItem(getFirstItem(oSubmenu));
1561                                  menuNav._setActiveItem(getFirstItem(oSubmenu));
1562  
1563                              }
1564                              else {
1565  
1566                                  menuNav._focusItem(oItem);
1567                                  menuNav._setActiveItem(oItem);
1568  
1569                              }
1570  
1571                          }
1572                          else {    //    MenuItem
1573  
1574                              menuNav._focusItem(oItem);
1575                              menuNav._setActiveItem(oItem);
1576  
1577                          }
1578  
1579                      }
1580  
1581                  }
1582                  else if (!menuNav._isRoot(oActiveMenu)) {
1583                      menuNav._hideMenu(oActiveMenu, true);
1584                  }
1585  
1586  
1587                  bPreventDefault = true;
1588  
1589              break;
1590  
1591              case 39:    //    right arrow
1592  
1593                  if (isMenuLabel(oTarget)) {
1594  
1595                      oSubmenu = oTarget.next();
1596  
1597                      if (oSubmenu) {
1598  
1599                          menuNav._showMenu(oSubmenu);
1600                          menuNav._focusItem(getFirstItem(oSubmenu));
1601                          menuNav._setActiveItem(getFirstItem(oSubmenu));
1602  
1603                      }
1604  
1605                  }
1606                  else if (isHorizontalMenu(oRootMenu)) {
1607  
1608                      oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
1609                      oLI = getNextSibling(oSubmenu.get(PARENT_NODE));
1610                      oItem = getItem(oLI);
1611  
1612                      menuNav._hideAllSubmenus(oRootMenu);
1613  
1614                      if (oItem) {
1615  
1616                          if (isMenuLabel(oItem)) {    //    Menu label
1617  
1618                              oSubmenu = oItem.next();
1619  
1620                              if (oSubmenu) {
1621  
1622                                  menuNav._showMenu(oSubmenu);
1623                                  menuNav._focusItem(getFirstItem(oSubmenu));
1624                                  menuNav._setActiveItem(getFirstItem(oSubmenu));
1625  
1626                              }
1627                              else {
1628  
1629                                  menuNav._focusItem(oItem);
1630                                  menuNav._setActiveItem(oItem);
1631  
1632                              }
1633  
1634                          }
1635                          else {    //    MenuItem
1636  
1637                              menuNav._focusItem(oItem);
1638                              menuNav._setActiveItem(oItem);
1639  
1640                          }
1641  
1642                      }
1643  
1644                  }
1645  
1646                  bPreventDefault = true;
1647  
1648              break;
1649  
1650          }
1651  
1652  
1653          if (bPreventDefault) {
1654  
1655              //    Prevent the browser from scrolling the window
1656  
1657              event.preventDefault();
1658  
1659          }
1660  
1661      },
1662  
1663  
1664      /**
1665      * @method _onHorizontalMenuKeyDown
1666      * @description "keydown" event handler for horizontal menus.
1667      * @protected
1668      * @param {Object} event Object representing the DOM event.
1669      */
1670      _onHorizontalMenuKeyDown: function (event) {
1671  
1672          var menuNav = this,
1673              oActiveMenu = menuNav._activeMenu,
1674              oTarget = event.target,
1675              oFocusedItem = getItem(oTarget, true),
1676              bPreventDefault = false,
1677              nKeyCode = event.keyCode,
1678              oSubmenu;
1679  
1680  
1681          if (nKeyCode === 40) {
1682  
1683              menuNav._hideAllSubmenus(oActiveMenu);
1684  
1685              if (isMenuLabel(oFocusedItem)) {
1686  
1687                  oSubmenu = oFocusedItem.next();
1688  
1689                  if (oSubmenu) {
1690  
1691                      menuNav._showMenu(oSubmenu);
1692                      menuNav._focusItem(getFirstItem(oSubmenu));
1693                      menuNav._setActiveItem(getFirstItem(oSubmenu));
1694  
1695                  }
1696  
1697                  bPreventDefault = true;
1698  
1699              }
1700  
1701          }
1702  
1703  
1704          if (bPreventDefault) {
1705  
1706              //    Prevent the browser from scrolling the window
1707  
1708              event.preventDefault();
1709  
1710          }
1711  
1712      },
1713  
1714  
1715      //    Generic DOM Event handlers
1716  
1717  
1718      /**
1719      * @method _onMouseMove
1720      * @description "mousemove" event handler for the menu.
1721      * @protected
1722      * @param {Object} event Object representing the DOM event.
1723      */
1724      _onMouseMove: function (event) {
1725  
1726          var menuNav = this;
1727  
1728          //    Using a timer to set the value of the "_currentMouseX" property
1729          //    helps improve the reliability of the calculation used to set the
1730          //    value of the "_movingToSubmenu" property - especially in Opera.
1731  
1732          later(10, menuNav, function () {
1733  
1734              menuNav._currentMouseX = event.pageX;
1735  
1736          });
1737  
1738      },
1739  
1740  
1741      /**
1742      * @method _onMouseOver
1743      * @description "mouseover" event handler for the menu.
1744      * @protected
1745      * @param {Object} event Object representing the DOM event.
1746      */
1747      _onMouseOver: function (event) {
1748  
1749          var menuNav = this,
1750              oTarget,
1751              oMenu,
1752              oMenuLabel,
1753              oParentMenu,
1754              oMenuItem;
1755  
1756  
1757          if (menuNav._blockMouseEvent) {
1758              menuNav._blockMouseEvent = false;
1759          }
1760          else {
1761  
1762              oTarget = event.target;
1763              oMenu = getMenu(oTarget, true);
1764              oMenuLabel = getMenuLabel(oTarget, true);
1765              oMenuItem = getMenuItem(oTarget, true);
1766  
1767  
1768              if (handleMouseOverForNode(oMenu, oTarget)) {
1769  
1770                  menuNav._onMenuMouseOver(oMenu, event);
1771  
1772                  oMenu[HANDLED_MOUSEOVER] = true;
1773                  oMenu[HANDLED_MOUSEOUT] = false;
1774  
1775                  oParentMenu = getParentMenu(oMenu);
1776  
1777                  if (oParentMenu) {
1778  
1779                      oParentMenu[HANDLED_MOUSEOUT] = true;
1780                      oParentMenu[HANDLED_MOUSEOVER] = false;
1781  
1782                  }
1783  
1784              }
1785  
1786              if (handleMouseOverForNode(oMenuLabel, oTarget)) {
1787  
1788                  menuNav._onMenuLabelMouseOver(oMenuLabel, event);
1789  
1790                  oMenuLabel[HANDLED_MOUSEOVER] = true;
1791                  oMenuLabel[HANDLED_MOUSEOUT] = false;
1792  
1793              }
1794  
1795              if (handleMouseOverForNode(oMenuItem, oTarget)) {
1796  
1797                  menuNav._onMenuItemMouseOver(oMenuItem, event);
1798  
1799                  oMenuItem[HANDLED_MOUSEOVER] = true;
1800                  oMenuItem[HANDLED_MOUSEOUT] = false;
1801  
1802              }
1803  
1804          }
1805  
1806      },
1807  
1808  
1809      /**
1810      * @method _onMouseOut
1811      * @description "mouseout" event handler for the menu.
1812      * @protected
1813      * @param {Object} event Object representing the DOM event.
1814      */
1815      _onMouseOut: function (event) {
1816  
1817          var menuNav = this,
1818              oActiveMenu = menuNav._activeMenu,
1819              bMovingToSubmenu = false,
1820              oTarget,
1821              oRelatedTarget,
1822              oMenu,
1823              oMenuLabel,
1824              oSubmenu,
1825              oMenuItem;
1826  
1827  
1828          menuNav._movingToSubmenu =
1829                      (oActiveMenu && !isHorizontalMenu(oActiveMenu) &&
1830                          ((event.pageX - 5) > menuNav._currentMouseX));
1831  
1832          oTarget = event.target;
1833          oRelatedTarget = event.relatedTarget;
1834          oMenu = getMenu(oTarget, true);
1835          oMenuLabel = getMenuLabel(oTarget, true);
1836          oMenuItem = getMenuItem(oTarget, true);
1837  
1838  
1839          if (handleMouseOutForNode(oMenuLabel, oRelatedTarget)) {
1840  
1841              menuNav._onMenuLabelMouseOut(oMenuLabel, event);
1842  
1843              oMenuLabel[HANDLED_MOUSEOUT] = true;
1844              oMenuLabel[HANDLED_MOUSEOVER] = false;
1845  
1846          }
1847  
1848          if (handleMouseOutForNode(oMenuItem, oRelatedTarget)) {
1849  
1850              menuNav._onMenuItemMouseOut(oMenuItem, event);
1851  
1852              oMenuItem[HANDLED_MOUSEOUT] = true;
1853              oMenuItem[HANDLED_MOUSEOVER] = false;
1854  
1855          }
1856  
1857  
1858          if (oMenuLabel) {
1859  
1860              oSubmenu = oMenuLabel.next();
1861  
1862              if (oSubmenu && oRelatedTarget &&
1863                  (oRelatedTarget.compareTo(oSubmenu) ||
1864                      oSubmenu.contains(oRelatedTarget))) {
1865  
1866                  bMovingToSubmenu = true;
1867  
1868              }
1869  
1870          }
1871  
1872  
1873          if (handleMouseOutForNode(oMenu, oRelatedTarget) || bMovingToSubmenu) {
1874  
1875              menuNav._onMenuMouseOut(oMenu, event);
1876  
1877              oMenu[HANDLED_MOUSEOUT] = true;
1878              oMenu[HANDLED_MOUSEOVER] = false;
1879  
1880          }
1881  
1882      },
1883  
1884  
1885      /**
1886      * @method _toggleSubmenuDisplay
1887      * @description "mousedown," "keydown," and "click" event handler for the
1888      * menu used to toggle the display of a submenu.
1889      * @protected
1890      * @param {Object} event Object representing the DOM event.
1891      */
1892      _toggleSubmenuDisplay: function (event) {
1893  
1894          var menuNav = this,
1895              oTarget = event.target,
1896              oMenuLabel = getMenuLabel(oTarget, true),
1897              sType = event.type,
1898              oAnchor,
1899              oSubmenu,
1900              sHref,
1901              nHashPos,
1902              nLen,
1903              sId;
1904  
1905  
1906          if (oMenuLabel) {
1907  
1908              oAnchor = isAnchor(oTarget) ? oTarget : oTarget.ancestor(isAnchor);
1909  
1910  
1911              if (oAnchor) {
1912  
1913                  //    Need to pass "2" as a second argument to "getAttribute" for
1914                  //    IE otherwise IE will return a fully qualified URL for the
1915                  //    value of the "href" attribute.
1916                  //    http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
1917  
1918                  sHref = oAnchor.getAttribute("href", 2);
1919                  nHashPos = sHref.indexOf("#");
1920                  nLen = sHref.length;
1921  
1922                  if (nHashPos === 0 && nLen > 1) {
1923  
1924                      sId = sHref.substr(1, nLen);
1925                      oSubmenu = oMenuLabel.next();
1926  
1927                      if (oSubmenu && (oSubmenu.get(ID) === sId)) {
1928  
1929                          if (sType === MOUSEDOWN || sType === KEYDOWN) {
1930  
1931                              if ((UA.opera || UA.gecko || UA.ie) && sType === KEYDOWN && !menuNav._preventClickHandle) {
1932  
1933                                  //    Prevent the browser from following the URL of
1934                                  //    the anchor element
1935  
1936                                  menuNav._preventClickHandle = menuNav._rootMenu.on("click", function (event) {
1937  
1938                                      event.preventDefault();
1939  
1940                                      menuNav._preventClickHandle.detach();
1941                                      menuNav._preventClickHandle = null;
1942  
1943                                  });
1944  
1945                              }
1946  
1947                              if (sType == MOUSEDOWN) {
1948  
1949                                  //    Prevent the target from getting focused by
1950                                  //    default, since the element to be focused will
1951                                  //    be determined by weather or not the submenu
1952                                  //    is visible.
1953                                  event.preventDefault();
1954  
1955                                  //    FocusManager will attempt to focus any
1956                                  //    descendant that is the target of the mousedown
1957                                  //    event.  Since we want to explicitly control
1958                                   //    where focus is going, we need to call
1959                                  //    "stopImmediatePropagation" to stop the
1960                                  //    FocusManager from doing its thing.
1961                                  event.stopImmediatePropagation();
1962  
1963                                  //    The "_focusItem" method relies on the
1964                                  //    "_hasFocus" property being set to true.  The
1965                                  //    "_hasFocus" property is normally set via a
1966                                  //    "focus" event listener, but since we've
1967                                  //    blocked focus from happening, we need to set
1968                                  //    this property manually.
1969                                  menuNav._hasFocus = true;
1970  
1971                              }
1972  
1973  
1974                              if (menuNav._isRoot(getParentMenu(oTarget))) {    //    Event target is a submenu label in the root menu
1975  
1976                                  //    Menu label toggle functionality
1977  
1978                                  if (hasVisibleSubmenu(oMenuLabel)) {
1979  
1980                                      menuNav._hideMenu(oSubmenu);
1981                                      menuNav._focusItem(oMenuLabel);
1982                                      menuNav._setActiveItem(oMenuLabel);
1983  
1984                                  }
1985                                  else {
1986  
1987                                      menuNav._hideAllSubmenus(menuNav._rootMenu);
1988                                      menuNav._showMenu(oSubmenu);
1989  
1990                                      menuNav._focusItem(getFirstItem(oSubmenu));
1991                                      menuNav._setActiveItem(getFirstItem(oSubmenu));
1992  
1993                                  }
1994  
1995                              }
1996                              else {    //    Event target is a submenu label within a submenu
1997  
1998                                  if (menuNav._activeItem == oMenuLabel) {
1999  
2000                                      menuNav._showMenu(oSubmenu);
2001                                      menuNav._focusItem(getFirstItem(oSubmenu));
2002                                      menuNav._setActiveItem(getFirstItem(oSubmenu));
2003  
2004                                  }
2005                                  else {
2006  
2007                                      if (!oMenuLabel._clickHandle) {
2008  
2009                                          oMenuLabel._clickHandle = oMenuLabel.on("click", function () {
2010  
2011                                              menuNav._hideAllSubmenus(menuNav._rootMenu);
2012  
2013                                              menuNav._hasFocus = false;
2014                                              menuNav._clearActiveItem();
2015  
2016  
2017                                              oMenuLabel._clickHandle.detach();
2018  
2019                                              oMenuLabel._clickHandle = null;
2020  
2021                                          });
2022  
2023                                      }
2024  
2025                                  }
2026  
2027                              }
2028  
2029                          }
2030  
2031  
2032                          if (sType === CLICK) {
2033  
2034                              //    Prevent the browser from following the URL of
2035                              //    the anchor element
2036  
2037                              event.preventDefault();
2038  
2039                          }
2040  
2041                      }
2042  
2043                  }
2044  
2045  
2046              }
2047  
2048          }
2049  
2050      },
2051  
2052  
2053      /**
2054      * @method _onKeyPress
2055      * @description "keypress" event handler for the menu.
2056      * @protected
2057      * @param {Object} event Object representing the DOM event.
2058      */
2059      _onKeyPress: function (event) {
2060  
2061          switch (event.keyCode) {
2062  
2063              case 37:    //    left arrow
2064              case 38:    //    up arrow
2065              case 39:    //    right arrow
2066              case 40:    //    down arrow
2067  
2068                  //    Prevent the browser from scrolling the window
2069  
2070                  event.preventDefault();
2071  
2072              break;
2073  
2074          }
2075  
2076      },
2077  
2078  
2079      /**
2080      * @method _onKeyDown
2081      * @description "keydown" event handler for the menu.
2082      * @protected
2083      * @param {Object} event Object representing the DOM event.
2084      */
2085      _onKeyDown: function (event) {
2086  
2087          var menuNav = this,
2088              oActiveItem = menuNav._activeItem,
2089              oTarget = event.target,
2090              oActiveMenu = getParentMenu(oTarget),
2091              oSubmenu;
2092  
2093          if (oActiveMenu) {
2094  
2095              menuNav._activeMenu = oActiveMenu;
2096  
2097              if (isHorizontalMenu(oActiveMenu)) {
2098                  menuNav._onHorizontalMenuKeyDown(event);
2099              }
2100              else {
2101                  menuNav._onVerticalMenuKeyDown(event);
2102              }
2103  
2104  
2105              if (event.keyCode === 27) {
2106  
2107                  if (!menuNav._isRoot(oActiveMenu)) {
2108  
2109                      if (UA.opera) {
2110                          later(0, menuNav, function () {
2111                              menuNav._hideMenu(oActiveMenu, true);
2112                          });
2113                      }
2114                      else {
2115                          menuNav._hideMenu(oActiveMenu, true);
2116                      }
2117  
2118                      event.stopPropagation();
2119                      menuNav._blockMouseEvent = UA.gecko ? true : false;
2120  
2121                  }
2122                  else if (oActiveItem) {
2123  
2124                      if (isMenuLabel(oActiveItem) &&
2125                              hasVisibleSubmenu(oActiveItem)) {
2126  
2127                          oSubmenu = oActiveItem.next();
2128  
2129                          if (oSubmenu) {
2130                              menuNav._hideMenu(oSubmenu);
2131                          }
2132  
2133                      }
2134                      else {
2135  
2136                          menuNav._focusManager.blur();
2137  
2138                          //    This is necessary for Webkit since blurring the
2139                          //    active menuitem won't result in the document
2140                          //    gaining focus, meaning the that _onDocFocus
2141                          //    listener won't clear the active menuitem.
2142  
2143                          menuNav._clearActiveItem();
2144  
2145                          menuNav._hasFocus = false;
2146  
2147                      }
2148  
2149                  }
2150  
2151              }
2152  
2153          }
2154  
2155      },
2156  
2157      /**
2158      * @method _onDocMouseDown
2159      * @description "mousedown" event handler for the owner document of
2160      * the menu.
2161      * @protected
2162      * @param {Object} event Object representing the DOM event.
2163      */
2164      _onDocMouseDown: function (event) {
2165  
2166          var menuNav = this,
2167              oRoot = menuNav._rootMenu,
2168              oTarget = event.target;
2169  
2170  
2171          if (!(oRoot.compareTo(oTarget) || oRoot.contains(oTarget))) {
2172  
2173              menuNav._hideAllSubmenus(oRoot);
2174  
2175              //    Document doesn't receive focus in Webkit when the user mouses
2176              //    down on it, so the "_hasFocus" property won't get set to the
2177              //    correct value.  The following line corrects the problem.
2178  
2179              if (UA.webkit) {
2180                  menuNav._hasFocus = false;
2181                  menuNav._clearActiveItem();
2182              }
2183  
2184          }
2185  
2186      }
2187  
2188  });
2189  
2190  
2191  Y.namespace('Plugin');
2192  
2193  Y.Plugin.NodeMenuNav = NodeMenuNav;
2194  
2195  
2196  }, '3.17.2', {"requires": ["node", "classnamemanager", "plugin", "node-focusmanager"], "skinnable": true});


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