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