[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/node-menunav/ -> node-menunav.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  
 710          if (oRootMenu) {
 711  
 712              menuNav._rootMenu = oRootMenu;
 713  
 714              oRootMenu.all("ul:first-child").addClass(FIRST_OF_TYPE);
 715  
 716              //    Hide all visible submenus
 717  
 718              oRootMenu.all(MENU_SELECTOR).addClass(CSS_MENU_HIDDEN);
 719  
 720  
 721              //    Wire up all event handlers
 722  
 723              aHandlers.push(oRootMenu.on("mouseover", menuNav._onMouseOver, menuNav));
 724              aHandlers.push(oRootMenu.on("mouseout", menuNav._onMouseOut, menuNav));
 725              aHandlers.push(oRootMenu.on("mousemove", menuNav._onMouseMove, menuNav));
 726              aHandlers.push(oRootMenu.on(MOUSEDOWN, menuNav._toggleSubmenuDisplay, menuNav));
 727              aHandlers.push(Y.on("key", menuNav._toggleSubmenuDisplay, oRootMenu, "down:13", menuNav));
 728              aHandlers.push(oRootMenu.on(CLICK, menuNav._toggleSubmenuDisplay, menuNav));
 729              aHandlers.push(oRootMenu.on("keypress", menuNav._onKeyPress, menuNav));
 730              aHandlers.push(oRootMenu.on(KEYDOWN, menuNav._onKeyDown, menuNav));
 731  
 732              oDoc = oRootMenu.get("ownerDocument");
 733  
 734              aHandlers.push(oDoc.on(MOUSEDOWN, menuNav._onDocMouseDown, menuNav));
 735              aHandlers.push(oDoc.on("focus", menuNav._onDocFocus, menuNav));
 736  
 737              this._eventHandlers = aHandlers;
 738  
 739              menuNav._initFocusManager();
 740  
 741          }
 742  
 743  
 744      },
 745  
 746      destructor: function () {
 747  
 748          var aHandlers = this._eventHandlers;
 749  
 750          if (aHandlers) {
 751  
 752              Y.Array.each(aHandlers, function (handle) {
 753                  handle.detach();
 754              });
 755  
 756              this._eventHandlers = null;
 757  
 758          }
 759  
 760          this.get(HOST).unplug("focusManager");
 761  
 762      },
 763  
 764  
 765  
 766      //    Protected methods
 767  
 768      /**
 769      * @method _isRoot
 770      * @description Returns a boolean indicating if the specified menu is the
 771      * root menu in the menu.
 772      * @protected
 773      * @param {Node} menu Node instance representing a menu.
 774      * @return {Boolean} Boolean indicating if the specified menu is the root
 775      * menu in the menu.
 776      */
 777      _isRoot: function (menu) {
 778  
 779          return this._rootMenu.compareTo(menu);
 780  
 781      },
 782  
 783  
 784      /**
 785      * @method _getTopmostSubmenu
 786      * @description Returns the topmost submenu of a submenu hierarchy.
 787      * @protected
 788      * @param {Node} menu Node instance representing a menu.
 789      * @return {Node} Node instance representing a menu.
 790      */
 791      _getTopmostSubmenu: function (menu) {
 792  
 793          var menuNav = this,
 794              oMenu = getParentMenu(menu),
 795              returnVal;
 796  
 797  
 798          if (!oMenu) {
 799              returnVal = menu;
 800          }
 801          else if (menuNav._isRoot(oMenu)) {
 802              returnVal = menu;
 803          }
 804          else {
 805              returnVal = menuNav._getTopmostSubmenu(oMenu);
 806          }
 807  
 808          return returnVal;
 809  
 810      },
 811  
 812  
 813      /**
 814      * @method _clearActiveItem
 815      * @description Clears the menu's active descendent.
 816      * @protected
 817      */
 818      _clearActiveItem: function () {
 819  
 820          var menuNav = this,
 821              oActiveItem = menuNav._activeItem;
 822  
 823          if (oActiveItem) {
 824              oActiveItem.removeClass(getActiveClass(oActiveItem));
 825          }
 826  
 827          menuNav._activeItem = null;
 828  
 829      },
 830  
 831  
 832      /**
 833      * @method _setActiveItem
 834      * @description Sets the specified menuitem or menu label as the menu's
 835      * active descendent.
 836      * @protected
 837      * @param {Node} item Node instance representing a menuitem or menu label.
 838      */
 839      _setActiveItem: function (item) {
 840  
 841          var menuNav = this;
 842  
 843          if (item) {
 844  
 845              menuNav._clearActiveItem();
 846  
 847              item.addClass(getActiveClass(item));
 848  
 849              menuNav._activeItem = item;
 850  
 851          }
 852  
 853      },
 854  
 855  
 856      /**
 857      * @method _focusItem
 858      * @description Focuses the specified menuitem or menu label.
 859      * @protected
 860      * @param {Node} item Node instance representing a menuitem or menu label.
 861      */
 862      _focusItem: function (item) {
 863  
 864          var menuNav = this,
 865              oMenu,
 866              oItem;
 867  
 868          if (item && menuNav._hasFocus) {
 869  
 870              oMenu = getParentMenu(item);
 871              oItem = getItemAnchor(item);
 872  
 873              if (oMenu && !oMenu.compareTo(menuNav._activeMenu)) {
 874                  menuNav._activeMenu = oMenu;
 875                  menuNav._initFocusManager();
 876              }
 877  
 878              menuNav._focusManager.focus(oItem);
 879  
 880          }
 881  
 882      },
 883  
 884  
 885      /**
 886      * @method _showMenu
 887      * @description Shows the specified menu.
 888      * @protected
 889      * @param {Node} menu Node instance representing a menu.
 890      */
 891      _showMenu: function (menu) {
 892  
 893          var oParentMenu = getParentMenu(menu),
 894              oLI = menu.get(PARENT_NODE),
 895              aXY = oLI.getXY();
 896  
 897  
 898          if (this.get(USE_ARIA)) {
 899              menu.set(ARIA_HIDDEN, false);
 900          }
 901  
 902  
 903          if (isHorizontalMenu(oParentMenu)) {
 904              aXY[1] = aXY[1] + oLI.get(OFFSET_HEIGHT);
 905          }
 906          else {
 907              aXY[0] = aXY[0] + oLI.get(OFFSET_WIDTH);
 908          }
 909  
 910          menu.setXY(aXY);
 911  
 912          if (UA.ie && UA.ie < 8) {
 913  
 914              if (UA.ie === 6 && !menu.hasIFrameShim) {
 915  
 916                  menu.appendChild(Y.Node.create(NodeMenuNav.SHIM_TEMPLATE));
 917                  menu.hasIFrameShim = true;
 918  
 919              }
 920  
 921              //    Clear previous values for height and width
 922  
 923              menu.setStyles({ height: EMPTY_STRING, width: EMPTY_STRING });
 924  
 925              //    Set the width and height of the menu's bounding box - this is
 926              //    necessary for IE 6 so that the CSS for the <iframe> shim can
 927              //    simply set the <iframe>'s width and height to 100% to ensure
 928              //    that dimensions of an <iframe> shim are always sync'd to the
 929              //    that of its parent menu.  Specifying a width and height also
 930              //    helps when positioning decorator elements (for creating effects
 931              //    like rounded corners) inside a menu's bounding box in IE 7.
 932  
 933              menu.setStyles({
 934                  height: (menu.get(OFFSET_HEIGHT) + PX),
 935                  width: (menu.get(OFFSET_WIDTH) + PX) });
 936  
 937          }
 938  
 939          menu.previous().addClass(CSS_MENU_LABEL_MENUVISIBLE);
 940          menu.removeClass(CSS_MENU_HIDDEN);
 941  
 942      },
 943  
 944  
 945      /**
 946      * @method _hideMenu
 947      * @description Hides the specified menu.
 948      * @protected
 949      * @param {Node} menu Node instance representing a menu.
 950      * @param {Boolean} activateAndFocusLabel Boolean indicating if the label
 951      * for the specified
 952      * menu should be focused and set as active.
 953      */
 954      _hideMenu: function (menu, activateAndFocusLabel) {
 955  
 956          var menuNav = this,
 957              oLabel = menu.previous(),
 958              oActiveItem;
 959  
 960          oLabel.removeClass(CSS_MENU_LABEL_MENUVISIBLE);
 961  
 962  
 963          if (activateAndFocusLabel) {
 964              menuNav._focusItem(oLabel);
 965              menuNav._setActiveItem(oLabel);
 966          }
 967  
 968          oActiveItem = menu.one((PERIOD + CSS_MENUITEM_ACTIVE));
 969  
 970          if (oActiveItem) {
 971              oActiveItem.removeClass(CSS_MENUITEM_ACTIVE);
 972          }
 973  
 974          //    Clear the values for top and left that were set by the call to
 975          //    "setXY" when the menu was shown so that the hidden position
 976          //    specified in the core CSS file will take affect.
 977  
 978          menu.setStyles({ left: EMPTY_STRING, top: EMPTY_STRING });
 979  
 980          menu.addClass(CSS_MENU_HIDDEN);
 981  
 982          if (menuNav.get(USE_ARIA)) {
 983              menu.set(ARIA_HIDDEN, true);
 984          }
 985  
 986      },
 987  
 988  
 989      /**
 990      * @method _hideAllSubmenus
 991      * @description Hides all submenus of the specified menu.
 992      * @protected
 993      * @param {Node} menu Node instance representing a menu.
 994      */
 995      _hideAllSubmenus: function (menu) {
 996  
 997          var menuNav = this;
 998  
 999          menu.all(MENU_SELECTOR).each(Y.bind(function (submenuNode) {
1000  
1001              menuNav._hideMenu(submenuNode);
1002  
1003          }, menuNav));
1004  
1005      },
1006  
1007  
1008      /**
1009      * @method _cancelShowSubmenuTimer
1010      * @description Cancels the timer used to show a submenu.
1011      * @protected
1012      */
1013      _cancelShowSubmenuTimer: function () {
1014  
1015          var menuNav = this,
1016              oShowSubmenuTimer = menuNav._showSubmenuTimer;
1017  
1018          if (oShowSubmenuTimer) {
1019              oShowSubmenuTimer.cancel();
1020              menuNav._showSubmenuTimer = null;
1021          }
1022  
1023      },
1024  
1025  
1026      /**
1027      * @method _cancelHideSubmenuTimer
1028      * @description Cancels the timer used to hide a submenu.
1029      * @protected
1030      */
1031      _cancelHideSubmenuTimer: function () {
1032  
1033          var menuNav = this,
1034              oHideSubmenuTimer = menuNav._hideSubmenuTimer;
1035  
1036  
1037          if (oHideSubmenuTimer) {
1038              oHideSubmenuTimer.cancel();
1039              menuNav._hideSubmenuTimer = null;
1040          }
1041  
1042      },
1043  
1044  
1045      /**
1046      * @method _initFocusManager
1047      * @description Initializes and updates the Focus Manager so that is is
1048      * always managing descendants of the active menu.
1049      * @protected
1050      */
1051      _initFocusManager: function () {
1052  
1053          var menuNav = this,
1054              oRootMenu = menuNav._rootMenu,
1055              oMenu = menuNav._activeMenu || oRootMenu,
1056              sSelectorBase =
1057                  menuNav._isRoot(oMenu) ? EMPTY_STRING : ("#" + oMenu.get("id")),
1058              oFocusManager = menuNav._focusManager,
1059              sKeysVal,
1060              sDescendantSelector,
1061              sQuery;
1062  
1063          if (isHorizontalMenu(oMenu)) {
1064  
1065              sDescendantSelector = sSelectorBase + STANDARD_QUERY + "," +
1066                  sSelectorBase + EXTENDED_QUERY;
1067  
1068              sKeysVal = { next: "down:39", previous: "down:37" };
1069  
1070          }
1071          else {
1072  
1073              sDescendantSelector = sSelectorBase + STANDARD_QUERY;
1074              sKeysVal = { next: "down:40", previous: "down:38" };
1075  
1076          }
1077  
1078  
1079          if (!oFocusManager) {
1080  
1081              oRootMenu.plug(Y.Plugin.NodeFocusManager, {
1082                  descendants: sDescendantSelector,
1083                  keys: sKeysVal,
1084                  circular: true
1085              });
1086  
1087              oFocusManager = oRootMenu.focusManager;
1088  
1089              sQuery = "#" + oRootMenu.get("id") + MENU_SELECTOR + " a," +
1090                              MENU_TOGGLE_SELECTOR;
1091  
1092              oRootMenu.all(sQuery).set("tabIndex", -1);
1093  
1094              oFocusManager.on(ACTIVE_DESCENDANT_CHANGE,
1095                  this._onActiveDescendantChange, oFocusManager, this);
1096  
1097              oFocusManager.after(ACTIVE_DESCENDANT_CHANGE,
1098                  this._afterActiveDescendantChange, oFocusManager, this);
1099  
1100              menuNav._focusManager = oFocusManager;
1101  
1102          }
1103          else {
1104  
1105              oFocusManager.set(ACTIVE_DESCENDANT, -1);
1106              oFocusManager.set(DESCENDANTS, sDescendantSelector);
1107              oFocusManager.set("keys", sKeysVal);
1108  
1109          }
1110  
1111      },
1112  
1113  
1114      //    Event handlers for discrete pieces of pieces of the menu
1115  
1116  
1117      /**
1118      * @method _onActiveDescendantChange
1119      * @description "activeDescendantChange" event handler for menu's
1120      * Focus Manager.
1121      * @protected
1122      * @param {Object} event Object representing the Attribute change event.
1123      * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
1124      */
1125      _onActiveDescendantChange: function (event, menuNav) {
1126  
1127          if (event.src === UI && menuNav._activeMenu &&
1128                  !menuNav._movingToSubmenu) {
1129  
1130              menuNav._hideAllSubmenus(menuNav._activeMenu);
1131  
1132          }
1133  
1134      },
1135  
1136  
1137      /**
1138      * @method _afterActiveDescendantChange
1139      * @description "activeDescendantChange" event handler for menu's
1140      * Focus Manager.
1141      * @protected
1142      * @param {Object} event Object representing the Attribute change event.
1143      * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
1144      */
1145      _afterActiveDescendantChange: function (event, menuNav) {
1146  
1147          var oItem;
1148  
1149          if (event.src === UI) {
1150              oItem = getItem(this.get(DESCENDANTS).item(event.newVal), true);
1151              menuNav._setActiveItem(oItem);
1152          }
1153  
1154      },
1155  
1156  
1157      /**
1158      * @method _onDocFocus
1159      * @description "focus" event handler for the owner document of the MenuNav.
1160      * @protected
1161      * @param {Object} event Object representing the DOM event.
1162      */
1163      _onDocFocus: function (event) {
1164  
1165          var menuNav = this,
1166              oActiveItem = menuNav._activeItem,
1167              oTarget = event.target,
1168              oMenu;
1169  
1170  
1171          if (menuNav._rootMenu.contains(oTarget)) {    //    The menu has focus
1172  
1173              if (menuNav._hasFocus) {
1174  
1175                  oMenu = getParentMenu(oTarget);
1176  
1177                  //    If the element that was focused is a descendant of the
1178                  //    root menu, but is in a submenu not currently being
1179                  //    managed by the Focus Manager, update the Focus Manager so
1180                  //    that it is now managing the submenu that is the parent of
1181                  //    the element that was focused.
1182  
1183                  if (!menuNav._activeMenu.compareTo(oMenu)) {
1184  
1185                      menuNav._activeMenu = oMenu;
1186                      menuNav._initFocusManager();
1187                      menuNav._focusManager.set(ACTIVE_DESCENDANT, oTarget);
1188                      menuNav._setActiveItem(getItem(oTarget, true));
1189  
1190                  }
1191  
1192              }
1193              else { //    Initial focus
1194  
1195                  //    First time the menu has been focused, need to setup focused
1196                  //    state and established active active descendant
1197  
1198                  menuNav._hasFocus = true;
1199  
1200                  oActiveItem = getItem(oTarget, true);
1201  
1202                  if (oActiveItem) {
1203                      menuNav._setActiveItem(oActiveItem);
1204                  }
1205  
1206              }
1207  
1208          }
1209          else {    //    The menu has lost focus
1210  
1211              menuNav._clearActiveItem();
1212  
1213              menuNav._cancelShowSubmenuTimer();
1214              menuNav._hideAllSubmenus(menuNav._rootMenu);
1215  
1216              menuNav._activeMenu = menuNav._rootMenu;
1217              menuNav._initFocusManager();
1218  
1219              menuNav._focusManager.set(ACTIVE_DESCENDANT, 0);
1220  
1221              menuNav._hasFocus = false;
1222  
1223          }
1224  
1225      },
1226  
1227  
1228      /**
1229      * @method _onMenuMouseOver
1230      * @description "mouseover" event handler for a menu.
1231      * @protected
1232      * @param {Node} menu Node instance representing a menu.
1233      * @param {Object} event Object representing the DOM event.
1234      */
1235      _onMenuMouseOver: function (menu, event) {
1236  
1237          var menuNav = this,
1238              oHideAllSubmenusTimer = menuNav._hideAllSubmenusTimer;
1239  
1240          if (oHideAllSubmenusTimer) {
1241              oHideAllSubmenusTimer.cancel();
1242              menuNav._hideAllSubmenusTimer = null;
1243          }
1244  
1245          menuNav._cancelHideSubmenuTimer();
1246  
1247          //    Need to update the FocusManager in advance of focus a new
1248          //    Menu in order to avoid the FocusManager thinking that
1249          //    it has lost focus
1250  
1251          if (menu && !menu.compareTo(menuNav._activeMenu)) {
1252              menuNav._activeMenu = menu;
1253  
1254              if (menuNav._hasFocus) {
1255                  menuNav._initFocusManager();
1256              }
1257  
1258          }
1259  
1260          if (menuNav._movingToSubmenu && isHorizontalMenu(menu)) {
1261              menuNav._movingToSubmenu = false;
1262          }
1263  
1264      },
1265  
1266  
1267      /**
1268      * @method _hideAndFocusLabel
1269      * @description Hides all of the submenus of the root menu and focuses the
1270      * label of the topmost submenu
1271      * @protected
1272      */
1273      _hideAndFocusLabel: function () {
1274  
1275          var    menuNav = this,
1276              oActiveMenu = menuNav._activeMenu,
1277              oSubmenu;
1278  
1279          menuNav._hideAllSubmenus(menuNav._rootMenu);
1280  
1281          if (oActiveMenu) {
1282  
1283              //    Focus the label element for the topmost submenu
1284              oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
1285              menuNav._focusItem(oSubmenu.previous());
1286  
1287          }
1288  
1289      },
1290  
1291  
1292      /**
1293      * @method _onMenuMouseOut
1294      * @description "mouseout" event handler for a menu.
1295      * @protected
1296      * @param {Node} menu Node instance representing a menu.
1297      * @param {Object} event Object representing the DOM event.
1298      */
1299      _onMenuMouseOut: function (menu, event) {
1300  
1301          var menuNav = this,
1302              oActiveMenu = menuNav._activeMenu,
1303              oRelatedTarget = event.relatedTarget,
1304              oActiveItem = menuNav._activeItem,
1305              oParentMenu,
1306              oMenu;
1307  
1308  
1309          if (oActiveMenu && !oActiveMenu.contains(oRelatedTarget)) {
1310  
1311              oParentMenu = getParentMenu(oActiveMenu);
1312  
1313  
1314              if (oParentMenu && !oParentMenu.contains(oRelatedTarget)) {
1315  
1316                  if (menuNav.get(MOUSEOUT_HIDE_DELAY) > 0) {
1317  
1318                      menuNav._cancelShowSubmenuTimer();
1319  
1320                      menuNav._hideAllSubmenusTimer =
1321  
1322                          later(menuNav.get(MOUSEOUT_HIDE_DELAY),
1323                              menuNav, menuNav._hideAndFocusLabel);
1324  
1325                  }
1326  
1327              }
1328              else {
1329  
1330                  if (oActiveItem) {
1331  
1332                      oMenu = getParentMenu(oActiveItem);
1333  
1334                      if (!menuNav._isRoot(oMenu)) {
1335                          menuNav._focusItem(oMenu.previous());
1336                      }
1337  
1338                  }
1339  
1340              }
1341  
1342          }
1343  
1344      },
1345  
1346  
1347      /**
1348      * @method _onMenuLabelMouseOver
1349      * @description "mouseover" event handler for a menu label.
1350      * @protected
1351      * @param {Node} menuLabel Node instance representing a menu label.
1352      * @param {Object} event Object representing the DOM event.
1353      */
1354      _onMenuLabelMouseOver: function (menuLabel, event) {
1355  
1356          var menuNav = this,
1357              oActiveMenu = menuNav._activeMenu,
1358              bIsRoot = menuNav._isRoot(oActiveMenu),
1359              bUseAutoSubmenuDisplay =
1360                  (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
1361              submenuShowDelay = menuNav.get("submenuShowDelay"),
1362              oSubmenu;
1363  
1364  
1365          var showSubmenu = function (delay) {
1366  
1367              menuNav._cancelHideSubmenuTimer();
1368              menuNav._cancelShowSubmenuTimer();
1369  
1370              if (!hasVisibleSubmenu(menuLabel)) {
1371  
1372                  oSubmenu = menuLabel.next();
1373  
1374                  if (oSubmenu) {
1375                      menuNav._hideAllSubmenus(oActiveMenu);
1376                      menuNav._showSubmenuTimer = later(delay, menuNav, menuNav._showMenu, oSubmenu);
1377                  }
1378  
1379              }
1380  
1381          };
1382  
1383  
1384          menuNav._focusItem(menuLabel);
1385          menuNav._setActiveItem(menuLabel);
1386  
1387  
1388          if (bUseAutoSubmenuDisplay) {
1389  
1390              if (menuNav._movingToSubmenu) {
1391  
1392                  //  If the user is moving diagonally from a submenu to
1393                  //  another submenu and they then stop and pause on a
1394                  //  menu label for an amount of time equal to the amount of
1395                  //  time defined for the display of a submenu then show the
1396                  //  submenu immediately.
1397                  //  http://yuilibrary.com/projects/yui3/ticket/2528316
1398  
1399                  //Y.message("Pause path");
1400  
1401                  menuNav._hoverTimer = later(submenuShowDelay, menuNav, function () {
1402                      showSubmenu(0);
1403                  });
1404  
1405              }
1406              else {
1407                  showSubmenu(submenuShowDelay);
1408              }
1409  
1410          }
1411  
1412      },
1413  
1414  
1415      /**
1416      * @method _onMenuLabelMouseOut
1417      * @description "mouseout" event handler for a menu label.
1418      * @protected
1419      * @param {Node} menuLabel Node instance representing a menu label.
1420      * @param {Object} event Object representing the DOM event.
1421      */
1422      _onMenuLabelMouseOut: function (menuLabel, event) {
1423  
1424          var menuNav = this,
1425              bIsRoot = menuNav._isRoot(menuNav._activeMenu),
1426              bUseAutoSubmenuDisplay =
1427                  (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
1428  
1429              oRelatedTarget = event.relatedTarget,
1430              oSubmenu = menuLabel.next(),
1431              hoverTimer = menuNav._hoverTimer;
1432  
1433          if (hoverTimer) {
1434              hoverTimer.cancel();
1435          }
1436  
1437          menuNav._clearActiveItem();
1438  
1439          if (bUseAutoSubmenuDisplay) {
1440  
1441              if (menuNav._movingToSubmenu &&
1442                      !menuNav._showSubmenuTimer && oSubmenu) {
1443  
1444                  //    If the mouse is moving diagonally toward the submenu and
1445                  //    another submenu isn't in the process of being displayed
1446                  //    (via a timer), then hide the submenu via a timer to give
1447                  //    the user some time to reach the submenu.
1448  
1449                  menuNav._hideSubmenuTimer =
1450                      later(menuNav.get("submenuHideDelay"), menuNav,
1451                          menuNav._hideMenu, oSubmenu);
1452  
1453              }
1454              else if (!menuNav._movingToSubmenu && oSubmenu && (!oRelatedTarget ||
1455                      (oRelatedTarget &&
1456                          !oSubmenu.contains(oRelatedTarget) &&
1457                          !oRelatedTarget.compareTo(oSubmenu)))) {
1458  
1459                  //    If the mouse is not moving toward the submenu, cancel any
1460                  //    submenus that might be in the process of being displayed
1461                  //    (via a timer) and hide this submenu immediately.
1462  
1463                  menuNav._cancelShowSubmenuTimer();
1464  
1465                  menuNav._hideMenu(oSubmenu);
1466  
1467              }
1468  
1469          }
1470  
1471      },
1472  
1473  
1474      /**
1475      * @method _onMenuItemMouseOver
1476      * @description "mouseover" event handler for a menuitem.
1477      * @protected
1478      * @param {Node} menuItem Node instance representing a menuitem.
1479      * @param {Object} event Object representing the DOM event.
1480      */
1481      _onMenuItemMouseOver: function (menuItem, event) {
1482  
1483          var menuNav = this,
1484              oActiveMenu = menuNav._activeMenu,
1485              bIsRoot = menuNav._isRoot(oActiveMenu),
1486              bUseAutoSubmenuDisplay =
1487                  (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot);
1488  
1489  
1490          menuNav._focusItem(menuItem);
1491          menuNav._setActiveItem(menuItem);
1492  
1493  
1494          if (bUseAutoSubmenuDisplay && !menuNav._movingToSubmenu) {
1495  
1496              menuNav._hideAllSubmenus(oActiveMenu);
1497  
1498          }
1499  
1500      },
1501  
1502  
1503      /**
1504      * @method _onMenuItemMouseOut
1505      * @description "mouseout" event handler for a menuitem.
1506      * @protected
1507      * @param {Node} menuItem Node instance representing a menuitem.
1508      * @param {Object} event Object representing the DOM event.
1509      */
1510      _onMenuItemMouseOut: function (menuItem, event) {
1511  
1512          this._clearActiveItem();
1513  
1514      },
1515  
1516  
1517      /**
1518      * @method _onVerticalMenuKeyDown
1519      * @description "keydown" event handler for vertical menus.
1520      * @protected
1521      * @param {Object} event Object representing the DOM event.
1522      */
1523      _onVerticalMenuKeyDown: function (event) {
1524  
1525          var menuNav = this,
1526              oActiveMenu = menuNav._activeMenu,
1527              oRootMenu = menuNav._rootMenu,
1528              oTarget = event.target,
1529              bPreventDefault = false,
1530              nKeyCode = event.keyCode,
1531              oSubmenu,
1532              oParentMenu,
1533              oLI,
1534              oItem;
1535  
1536  
1537          switch (nKeyCode) {
1538  
1539              case 37:    //    left arrow
1540  
1541                  oParentMenu = getParentMenu(oActiveMenu);
1542  
1543                  if (oParentMenu && isHorizontalMenu(oParentMenu)) {
1544  
1545                      menuNav._hideMenu(oActiveMenu);
1546                      oLI = getPreviousSibling(oActiveMenu.get(PARENT_NODE));
1547                      oItem = getItem(oLI);
1548  
1549                      if (oItem) {
1550  
1551                          if (isMenuLabel(oItem)) {    //    Menu label
1552  
1553                              oSubmenu = oItem.next();
1554  
1555  
1556                              if (oSubmenu) {
1557  
1558                                  menuNav._showMenu(oSubmenu);
1559                                  menuNav._focusItem(getFirstItem(oSubmenu));
1560                                  menuNav._setActiveItem(getFirstItem(oSubmenu));
1561  
1562                              }
1563                              else {
1564  
1565                                  menuNav._focusItem(oItem);
1566                                  menuNav._setActiveItem(oItem);
1567  
1568                              }
1569  
1570                          }
1571                          else {    //    MenuItem
1572  
1573                              menuNav._focusItem(oItem);
1574                              menuNav._setActiveItem(oItem);
1575  
1576                          }
1577  
1578                      }
1579  
1580                  }
1581                  else if (!menuNav._isRoot(oActiveMenu)) {
1582                      menuNav._hideMenu(oActiveMenu, true);
1583                  }
1584  
1585  
1586                  bPreventDefault = true;
1587  
1588              break;
1589  
1590              case 39:    //    right arrow
1591  
1592                  if (isMenuLabel(oTarget)) {
1593  
1594                      oSubmenu = oTarget.next();
1595  
1596                      if (oSubmenu) {
1597  
1598                          menuNav._showMenu(oSubmenu);
1599                          menuNav._focusItem(getFirstItem(oSubmenu));
1600                          menuNav._setActiveItem(getFirstItem(oSubmenu));
1601  
1602                      }
1603  
1604                  }
1605                  else if (isHorizontalMenu(oRootMenu)) {
1606  
1607                      oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
1608                      oLI = getNextSibling(oSubmenu.get(PARENT_NODE));
1609                      oItem = getItem(oLI);
1610  
1611                      menuNav._hideAllSubmenus(oRootMenu);
1612  
1613                      if (oItem) {
1614  
1615                          if (isMenuLabel(oItem)) {    //    Menu label
1616  
1617                              oSubmenu = oItem.next();
1618  
1619                              if (oSubmenu) {
1620  
1621                                  menuNav._showMenu(oSubmenu);
1622                                  menuNav._focusItem(getFirstItem(oSubmenu));
1623                                  menuNav._setActiveItem(getFirstItem(oSubmenu));
1624  
1625                              }
1626                              else {
1627  
1628                                  menuNav._focusItem(oItem);
1629                                  menuNav._setActiveItem(oItem);
1630  
1631                              }
1632  
1633                          }
1634                          else {    //    MenuItem
1635  
1636                              menuNav._focusItem(oItem);
1637                              menuNav._setActiveItem(oItem);
1638  
1639                          }
1640  
1641                      }
1642  
1643                  }
1644  
1645                  bPreventDefault = true;
1646  
1647              break;
1648  
1649          }
1650  
1651  
1652          if (bPreventDefault) {
1653  
1654              //    Prevent the browser from scrolling the window
1655  
1656              event.preventDefault();
1657  
1658          }
1659  
1660      },
1661  
1662  
1663      /**
1664      * @method _onHorizontalMenuKeyDown
1665      * @description "keydown" event handler for horizontal menus.
1666      * @protected
1667      * @param {Object} event Object representing the DOM event.
1668      */
1669      _onHorizontalMenuKeyDown: function (event) {
1670  
1671          var menuNav = this,
1672              oActiveMenu = menuNav._activeMenu,
1673              oTarget = event.target,
1674              oFocusedItem = getItem(oTarget, true),
1675              bPreventDefault = false,
1676              nKeyCode = event.keyCode,
1677              oSubmenu;
1678  
1679  
1680          if (nKeyCode === 40) {
1681  
1682              menuNav._hideAllSubmenus(oActiveMenu);
1683  
1684              if (isMenuLabel(oFocusedItem)) {
1685  
1686                  oSubmenu = oFocusedItem.next();
1687  
1688                  if (oSubmenu) {
1689  
1690                      menuNav._showMenu(oSubmenu);
1691                      menuNav._focusItem(getFirstItem(oSubmenu));
1692                      menuNav._setActiveItem(getFirstItem(oSubmenu));
1693  
1694                  }
1695  
1696                  bPreventDefault = true;
1697  
1698              }
1699  
1700          }
1701  
1702  
1703          if (bPreventDefault) {
1704  
1705              //    Prevent the browser from scrolling the window
1706  
1707              event.preventDefault();
1708  
1709          }
1710  
1711      },
1712  
1713  
1714      //    Generic DOM Event handlers
1715  
1716  
1717      /**
1718      * @method _onMouseMove
1719      * @description "mousemove" event handler for the menu.
1720      * @protected
1721      * @param {Object} event Object representing the DOM event.
1722      */
1723      _onMouseMove: function (event) {
1724  
1725          var menuNav = this;
1726  
1727          //    Using a timer to set the value of the "_currentMouseX" property
1728          //    helps improve the reliability of the calculation used to set the
1729          //    value of the "_movingToSubmenu" property - especially in Opera.
1730  
1731          later(10, menuNav, function () {
1732  
1733              menuNav._currentMouseX = event.pageX;
1734  
1735          });
1736  
1737      },
1738  
1739  
1740      /**
1741      * @method _onMouseOver
1742      * @description "mouseover" event handler for the menu.
1743      * @protected
1744      * @param {Object} event Object representing the DOM event.
1745      */
1746      _onMouseOver: function (event) {
1747  
1748          var menuNav = this,
1749              oTarget,
1750              oMenu,
1751              oMenuLabel,
1752              oParentMenu,
1753              oMenuItem;
1754  
1755  
1756          if (menuNav._blockMouseEvent) {
1757              menuNav._blockMouseEvent = false;
1758          }
1759          else {
1760  
1761              oTarget = event.target;
1762              oMenu = getMenu(oTarget, true);
1763              oMenuLabel = getMenuLabel(oTarget, true);
1764              oMenuItem = getMenuItem(oTarget, true);
1765  
1766  
1767              if (handleMouseOverForNode(oMenu, oTarget)) {
1768  
1769                  menuNav._onMenuMouseOver(oMenu, event);
1770  
1771                  oMenu[HANDLED_MOUSEOVER] = true;
1772                  oMenu[HANDLED_MOUSEOUT] = false;
1773  
1774                  oParentMenu = getParentMenu(oMenu);
1775  
1776                  if (oParentMenu) {
1777  
1778                      oParentMenu[HANDLED_MOUSEOUT] = true;
1779                      oParentMenu[HANDLED_MOUSEOVER] = false;
1780  
1781                  }
1782  
1783              }
1784  
1785              if (handleMouseOverForNode(oMenuLabel, oTarget)) {
1786  
1787                  menuNav._onMenuLabelMouseOver(oMenuLabel, event);
1788  
1789                  oMenuLabel[HANDLED_MOUSEOVER] = true;
1790                  oMenuLabel[HANDLED_MOUSEOUT] = false;
1791  
1792              }
1793  
1794              if (handleMouseOverForNode(oMenuItem, oTarget)) {
1795  
1796                  menuNav._onMenuItemMouseOver(oMenuItem, event);
1797  
1798                  oMenuItem[HANDLED_MOUSEOVER] = true;
1799                  oMenuItem[HANDLED_MOUSEOUT] = false;
1800  
1801              }
1802  
1803          }
1804  
1805      },
1806  
1807  
1808      /**
1809      * @method _onMouseOut
1810      * @description "mouseout" event handler for the menu.
1811      * @protected
1812      * @param {Object} event Object representing the DOM event.
1813      */
1814      _onMouseOut: function (event) {
1815  
1816          var menuNav = this,
1817              oActiveMenu = menuNav._activeMenu,
1818              bMovingToSubmenu = false,
1819              oTarget,
1820              oRelatedTarget,
1821              oMenu,
1822              oMenuLabel,
1823              oSubmenu,
1824              oMenuItem;
1825  
1826  
1827          menuNav._movingToSubmenu =
1828                      (oActiveMenu && !isHorizontalMenu(oActiveMenu) &&
1829                          ((event.pageX - 5) > menuNav._currentMouseX));
1830  
1831          oTarget = event.target;
1832          oRelatedTarget = event.relatedTarget;
1833          oMenu = getMenu(oTarget, true);
1834          oMenuLabel = getMenuLabel(oTarget, true);
1835          oMenuItem = getMenuItem(oTarget, true);
1836  
1837  
1838          if (handleMouseOutForNode(oMenuLabel, oRelatedTarget)) {
1839  
1840              menuNav._onMenuLabelMouseOut(oMenuLabel, event);
1841  
1842              oMenuLabel[HANDLED_MOUSEOUT] = true;
1843              oMenuLabel[HANDLED_MOUSEOVER] = false;
1844  
1845          }
1846  
1847          if (handleMouseOutForNode(oMenuItem, oRelatedTarget)) {
1848  
1849              menuNav._onMenuItemMouseOut(oMenuItem, event);
1850  
1851              oMenuItem[HANDLED_MOUSEOUT] = true;
1852              oMenuItem[HANDLED_MOUSEOVER] = false;
1853  
1854          }
1855  
1856  
1857          if (oMenuLabel) {
1858  
1859              oSubmenu = oMenuLabel.next();
1860  
1861              if (oSubmenu && oRelatedTarget &&
1862                  (oRelatedTarget.compareTo(oSubmenu) ||
1863                      oSubmenu.contains(oRelatedTarget))) {
1864  
1865                  bMovingToSubmenu = true;
1866  
1867              }
1868  
1869          }
1870  
1871  
1872          if (handleMouseOutForNode(oMenu, oRelatedTarget) || bMovingToSubmenu) {
1873  
1874              menuNav._onMenuMouseOut(oMenu, event);
1875  
1876              oMenu[HANDLED_MOUSEOUT] = true;
1877              oMenu[HANDLED_MOUSEOVER] = false;
1878  
1879          }
1880  
1881      },
1882  
1883  
1884      /**
1885      * @method _toggleSubmenuDisplay
1886      * @description "mousedown," "keydown," and "click" event handler for the
1887      * menu used to toggle the display of a submenu.
1888      * @protected
1889      * @param {Object} event Object representing the DOM event.
1890      */
1891      _toggleSubmenuDisplay: function (event) {
1892  
1893          var menuNav = this,
1894              oTarget = event.target,
1895              oMenuLabel = getMenuLabel(oTarget, true),
1896              sType = event.type,
1897              oAnchor,
1898              oSubmenu,
1899              sHref,
1900              nHashPos,
1901              nLen,
1902              sId;
1903  
1904  
1905          if (oMenuLabel) {
1906  
1907              oAnchor = isAnchor(oTarget) ? oTarget : oTarget.ancestor(isAnchor);
1908  
1909  
1910              if (oAnchor) {
1911  
1912                  //    Need to pass "2" as a second argument to "getAttribute" for
1913                  //    IE otherwise IE will return a fully qualified URL for the
1914                  //    value of the "href" attribute.
1915                  //    http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
1916  
1917                  sHref = oAnchor.getAttribute("href", 2);
1918                  nHashPos = sHref.indexOf("#");
1919                  nLen = sHref.length;
1920  
1921                  if (nHashPos === 0 && nLen > 1) {
1922  
1923                      sId = sHref.substr(1, nLen);
1924                      oSubmenu = oMenuLabel.next();
1925  
1926                      if (oSubmenu && (oSubmenu.get(ID) === sId)) {
1927  
1928                          if (sType === MOUSEDOWN || sType === KEYDOWN) {
1929  
1930                              if ((UA.opera || UA.gecko || UA.ie) && sType === KEYDOWN && !menuNav._preventClickHandle) {
1931  
1932                                  //    Prevent the browser from following the URL of
1933                                  //    the anchor element
1934  
1935                                  menuNav._preventClickHandle = menuNav._rootMenu.on("click", function (event) {
1936  
1937                                      event.preventDefault();
1938  
1939                                      menuNav._preventClickHandle.detach();
1940                                      menuNav._preventClickHandle = null;
1941  
1942                                  });
1943  
1944                              }
1945  
1946                              if (sType == MOUSEDOWN) {
1947  
1948                                  //    Prevent the target from getting focused by
1949                                  //    default, since the element to be focused will
1950                                  //    be determined by weather or not the submenu
1951                                  //    is visible.
1952                                  event.preventDefault();
1953  
1954                                  //    FocusManager will attempt to focus any
1955                                  //    descendant that is the target of the mousedown
1956                                  //    event.  Since we want to explicitly control
1957                                   //    where focus is going, we need to call
1958                                  //    "stopImmediatePropagation" to stop the
1959                                  //    FocusManager from doing its thing.
1960                                  event.stopImmediatePropagation();
1961  
1962                                  //    The "_focusItem" method relies on the
1963                                  //    "_hasFocus" property being set to true.  The
1964                                  //    "_hasFocus" property is normally set via a
1965                                  //    "focus" event listener, but since we've
1966                                  //    blocked focus from happening, we need to set
1967                                  //    this property manually.
1968                                  menuNav._hasFocus = true;
1969  
1970                              }
1971  
1972  
1973                              if (menuNav._isRoot(getParentMenu(oTarget))) {    //    Event target is a submenu label in the root menu
1974  
1975                                  //    Menu label toggle functionality
1976  
1977                                  if (hasVisibleSubmenu(oMenuLabel)) {
1978  
1979                                      menuNav._hideMenu(oSubmenu);
1980                                      menuNav._focusItem(oMenuLabel);
1981                                      menuNav._setActiveItem(oMenuLabel);
1982  
1983                                  }
1984                                  else {
1985  
1986                                      menuNav._hideAllSubmenus(menuNav._rootMenu);
1987                                      menuNav._showMenu(oSubmenu);
1988  
1989                                      menuNav._focusItem(getFirstItem(oSubmenu));
1990                                      menuNav._setActiveItem(getFirstItem(oSubmenu));
1991  
1992                                  }
1993  
1994                              }
1995                              else {    //    Event target is a submenu label within a submenu
1996  
1997                                  if (menuNav._activeItem == oMenuLabel) {
1998  
1999                                      menuNav._showMenu(oSubmenu);
2000                                      menuNav._focusItem(getFirstItem(oSubmenu));
2001                                      menuNav._setActiveItem(getFirstItem(oSubmenu));
2002  
2003                                  }
2004                                  else {
2005  
2006                                      if (!oMenuLabel._clickHandle) {
2007  
2008                                          oMenuLabel._clickHandle = oMenuLabel.on("click", function () {
2009  
2010                                              menuNav._hideAllSubmenus(menuNav._rootMenu);
2011  
2012                                              menuNav._hasFocus = false;
2013                                              menuNav._clearActiveItem();
2014  
2015  
2016                                              oMenuLabel._clickHandle.detach();
2017  
2018                                              oMenuLabel._clickHandle = null;
2019  
2020                                          });
2021  
2022                                      }
2023  
2024                                  }
2025  
2026                              }
2027  
2028                          }
2029  
2030  
2031                          if (sType === CLICK) {
2032  
2033                              //    Prevent the browser from following the URL of
2034                              //    the anchor element
2035  
2036                              event.preventDefault();
2037  
2038                          }
2039  
2040                      }
2041  
2042                  }
2043  
2044  
2045              }
2046  
2047          }
2048  
2049      },
2050  
2051  
2052      /**
2053      * @method _onKeyPress
2054      * @description "keypress" event handler for the menu.
2055      * @protected
2056      * @param {Object} event Object representing the DOM event.
2057      */
2058      _onKeyPress: function (event) {
2059  
2060          switch (event.keyCode) {
2061  
2062              case 37:    //    left arrow
2063              case 38:    //    up arrow
2064              case 39:    //    right arrow
2065              case 40:    //    down arrow
2066  
2067                  //    Prevent the browser from scrolling the window
2068  
2069                  event.preventDefault();
2070  
2071              break;
2072  
2073          }
2074  
2075      },
2076  
2077  
2078      /**
2079      * @method _onKeyDown
2080      * @description "keydown" event handler for the menu.
2081      * @protected
2082      * @param {Object} event Object representing the DOM event.
2083      */
2084      _onKeyDown: function (event) {
2085  
2086          var menuNav = this,
2087              oActiveItem = menuNav._activeItem,
2088              oTarget = event.target,
2089              oActiveMenu = getParentMenu(oTarget),
2090              oSubmenu;
2091  
2092          if (oActiveMenu) {
2093  
2094              menuNav._activeMenu = oActiveMenu;
2095  
2096              if (isHorizontalMenu(oActiveMenu)) {
2097                  menuNav._onHorizontalMenuKeyDown(event);
2098              }
2099              else {
2100                  menuNav._onVerticalMenuKeyDown(event);
2101              }
2102  
2103  
2104              if (event.keyCode === 27) {
2105  
2106                  if (!menuNav._isRoot(oActiveMenu)) {
2107  
2108                      if (UA.opera) {
2109                          later(0, menuNav, function () {
2110                              menuNav._hideMenu(oActiveMenu, true);
2111                          });
2112                      }
2113                      else {
2114                          menuNav._hideMenu(oActiveMenu, true);
2115                      }
2116  
2117                      event.stopPropagation();
2118                      menuNav._blockMouseEvent = UA.gecko ? true : false;
2119  
2120                  }
2121                  else if (oActiveItem) {
2122  
2123                      if (isMenuLabel(oActiveItem) &&
2124                              hasVisibleSubmenu(oActiveItem)) {
2125  
2126                          oSubmenu = oActiveItem.next();
2127  
2128                          if (oSubmenu) {
2129                              menuNav._hideMenu(oSubmenu);
2130                          }
2131  
2132                      }
2133                      else {
2134  
2135                          menuNav._focusManager.blur();
2136  
2137                          //    This is necessary for Webkit since blurring the
2138                          //    active menuitem won't result in the document
2139                          //    gaining focus, meaning the that _onDocFocus
2140                          //    listener won't clear the active menuitem.
2141  
2142                          menuNav._clearActiveItem();
2143  
2144                          menuNav._hasFocus = false;
2145  
2146                      }
2147  
2148                  }
2149  
2150              }
2151  
2152          }
2153  
2154      },
2155  
2156      /**
2157      * @method _onDocMouseDown
2158      * @description "mousedown" event handler for the owner document of
2159      * the menu.
2160      * @protected
2161      * @param {Object} event Object representing the DOM event.
2162      */
2163      _onDocMouseDown: function (event) {
2164  
2165          var menuNav = this,
2166              oRoot = menuNav._rootMenu,
2167              oTarget = event.target;
2168  
2169  
2170          if (!(oRoot.compareTo(oTarget) || oRoot.contains(oTarget))) {
2171  
2172              menuNav._hideAllSubmenus(oRoot);
2173  
2174              //    Document doesn't receive focus in Webkit when the user mouses
2175              //    down on it, so the "_hasFocus" property won't get set to the
2176              //    correct value.  The following line corrects the problem.
2177  
2178              if (UA.webkit) {
2179                  menuNav._hasFocus = false;
2180                  menuNav._clearActiveItem();
2181              }
2182  
2183          }
2184  
2185      }
2186  
2187  });
2188  
2189  
2190  Y.namespace('Plugin');
2191  
2192  Y.Plugin.NodeMenuNav = NodeMenuNav;
2193  
2194  
2195  }, '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