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