]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/menu/menu.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / yui / build / menu / menu.js
1 /*
2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 2.9.0
6 */
7
8
9 /**
10 * @module menu
11 * @description <p>The Menu family of components features a collection of 
12 * controls that make it easy to add menus to your website or web application.  
13 * With the Menu Controls you can create website fly-out menus, customized 
14 * context menus, or application-style menu bars with just a small amount of 
15 * scripting.</p><p>The Menu family of controls features:</p>
16 * <ul>
17 *    <li>Keyboard and mouse navigation.</li>
18 *    <li>A rich event model that provides access to all of a menu's 
19 *    interesting moments.</li>
20 *    <li>Support for 
21 *    <a href="http://en.wikipedia.org/wiki/Progressive_Enhancement">Progressive
22 *    Enhancement</a>; Menus can be created from simple, 
23 *    semantic markup on the page or purely through JavaScript.</li>
24 * </ul>
25 * @title Menu
26 * @namespace YAHOO.widget
27 * @requires Event, Dom, Container
28 */
29 (function () {
30
31     var UA = YAHOO.env.ua,
32         Dom = YAHOO.util.Dom,
33         Event = YAHOO.util.Event,
34         Lang = YAHOO.lang,
35
36         _DIV = "DIV",
37         _HD = "hd",
38         _BD = "bd",
39         _FT = "ft",
40         _LI = "LI",
41         _DISABLED = "disabled",
42         _MOUSEOVER = "mouseover",
43         _MOUSEOUT = "mouseout",
44         _MOUSEDOWN = "mousedown",
45         _MOUSEUP = "mouseup",
46         _CLICK = "click",
47         _KEYDOWN = "keydown",
48         _KEYUP = "keyup",
49         _KEYPRESS = "keypress",
50         _CLICK_TO_HIDE = "clicktohide",
51         _POSITION = "position", 
52         _DYNAMIC = "dynamic",
53         _SHOW_DELAY = "showdelay",
54         _SELECTED = "selected",
55         _VISIBLE = "visible",
56         _UL = "UL",
57         _MENUMANAGER = "MenuManager";
58
59
60     /**
61     * Singleton that manages a collection of all menus and menu items.  Listens 
62     * for DOM events at the document level and dispatches the events to the 
63     * corresponding menu or menu item.
64     *
65     * @namespace YAHOO.widget
66     * @class MenuManager
67     * @static
68     */
69     YAHOO.widget.MenuManager = function () {
70     
71         // Private member variables
72     
73     
74         // Flag indicating if the DOM event handlers have been attached
75     
76         var m_bInitializedEventHandlers = false,
77     
78     
79         // Collection of menus
80
81         m_oMenus = {},
82
83
84         // Collection of visible menus
85     
86         m_oVisibleMenus = {},
87     
88     
89         //  Collection of menu items 
90
91         m_oItems = {},
92
93
94         // Map of DOM event types to their equivalent CustomEvent types
95         
96         m_oEventTypes = {
97             "click": "clickEvent",
98             "mousedown": "mouseDownEvent",
99             "mouseup": "mouseUpEvent",
100             "mouseover": "mouseOverEvent",
101             "mouseout": "mouseOutEvent",
102             "keydown": "keyDownEvent",
103             "keyup": "keyUpEvent",
104             "keypress": "keyPressEvent",
105             "focus": "focusEvent",
106             "focusin": "focusEvent",
107             "blur": "blurEvent",
108             "focusout": "blurEvent"
109         },
110     
111     
112         m_oFocusedMenuItem = null;
113     
114     
115     
116         // Private methods
117     
118     
119         /**
120         * @method getMenuRootElement
121         * @description Finds the root DIV node of a menu or the root LI node of 
122         * a menu item.
123         * @private
124         * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
125         * level-one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object 
126         * specifying an HTML element.
127         */
128         function getMenuRootElement(p_oElement) {
129         
130             var oParentNode,
131                 returnVal;
132     
133             if (p_oElement && p_oElement.tagName) {
134             
135                 switch (p_oElement.tagName.toUpperCase()) {
136                         
137                 case _DIV:
138     
139                     oParentNode = p_oElement.parentNode;
140     
141                     // Check if the DIV is the inner "body" node of a menu
142
143                     if ((
144                             Dom.hasClass(p_oElement, _HD) ||
145                             Dom.hasClass(p_oElement, _BD) ||
146                             Dom.hasClass(p_oElement, _FT)
147                         ) && 
148                         oParentNode && 
149                         oParentNode.tagName && 
150                         oParentNode.tagName.toUpperCase() == _DIV) {
151                     
152                         returnVal = oParentNode;
153                     
154                     }
155                     else {
156                     
157                         returnVal = p_oElement;
158                     
159                     }
160                 
161                     break;
162
163                 case _LI:
164     
165                     returnVal = p_oElement;
166                     
167                     break;
168
169                 default:
170     
171                     oParentNode = p_oElement.parentNode;
172     
173                     if (oParentNode) {
174                     
175                         returnVal = getMenuRootElement(oParentNode);
176                     
177                     }
178                 
179                     break;
180                 
181                 }
182     
183             }
184             
185             return returnVal;
186             
187         }
188     
189     
190     
191         // Private event handlers
192     
193     
194         /**
195         * @method onDOMEvent
196         * @description Generic, global event handler for all of a menu's 
197         * DOM-based events.  This listens for events against the document 
198         * object.  If the target of a given event is a member of a menu or 
199         * menu item's DOM, the instance's corresponding Custom Event is fired.
200         * @private
201         * @param {Event} p_oEvent Object representing the DOM event object  
202         * passed back by the event utility (YAHOO.util.Event).
203         */
204         function onDOMEvent(p_oEvent) {
205     
206             // Get the target node of the DOM event
207         
208             var oTarget = Event.getTarget(p_oEvent),
209                 
210             // See if the target of the event was a menu, or a menu item
211     
212             oElement = getMenuRootElement(oTarget),
213             bFireEvent = true,
214             sEventType = p_oEvent.type,
215             sCustomEventType,
216             sTagName,
217             sId,
218             oMenuItem,
219             oMenu; 
220     
221     
222             if (oElement) {
223     
224                 sTagName = oElement.tagName.toUpperCase();
225         
226                 if (sTagName == _LI) {
227             
228                     sId = oElement.id;
229             
230                     if (sId && m_oItems[sId]) {
231             
232                         oMenuItem = m_oItems[sId];
233                         oMenu = oMenuItem.parent;
234             
235                     }
236                 
237                 }
238                 else if (sTagName == _DIV) {
239                 
240                     if (oElement.id) {
241                     
242                         oMenu = m_oMenus[oElement.id];
243                     
244                     }
245                 
246                 }
247     
248             }
249     
250     
251             if (oMenu) {
252     
253                 sCustomEventType = m_oEventTypes[sEventType];
254
255                 /*
256                     There is an inconsistency between Firefox for Mac OS X and 
257                     Firefox Windows & Linux regarding the triggering of the 
258                     display of the browser's context menu and the subsequent 
259                     firing of the "click" event. In Firefox for Windows & Linux, 
260                     when the user triggers the display of the browser's context 
261                     menu the "click" event also fires for the document object, 
262                     even though the "click" event did not fire for the element 
263                     that was the original target of the "contextmenu" event. 
264                     This is unique to Firefox on Windows & Linux.  For all 
265                     other A-Grade browsers, including Firefox for Mac OS X, the 
266                     "click" event doesn't fire for the document object. 
267
268                     This bug in Firefox for Windows affects Menu, as Menu 
269                     instances listen for events at the document level and 
270                     dispatches Custom Events of the same name.  Therefore users
271                     of Menu will get an unwanted firing of the "click" 
272                     custom event.  The following line fixes this bug.
273                 */
274                 
275
276
277                 if (sEventType == "click" && 
278                     (UA.gecko && oMenu.platform != "mac") && 
279                     p_oEvent.button > 0) {
280
281                     bFireEvent = false;
282
283                 }
284     
285                 // Fire the Custom Event that corresponds the current DOM event    
286         
287                 if (bFireEvent && oMenuItem && !oMenuItem.cfg.getProperty(_DISABLED)) {
288                     oMenuItem[sCustomEventType].fire(p_oEvent);                   
289                 }
290         
291                 if (bFireEvent) {
292                     oMenu[sCustomEventType].fire(p_oEvent, oMenuItem);
293                 }
294             
295             }
296             else if (sEventType == _MOUSEDOWN) {
297     
298                 /*
299                     If the target of the event wasn't a menu, hide all 
300                     dynamically positioned menus
301                 */
302                 
303                 for (var i in m_oVisibleMenus) {
304         
305                     if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {
306         
307                         oMenu = m_oVisibleMenus[i];
308
309                         if (oMenu.cfg.getProperty(_CLICK_TO_HIDE) && 
310                             !(oMenu instanceof YAHOO.widget.MenuBar) && 
311                             oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {
312
313                             oMenu.hide();
314
315                             //  In IE when the user mouses down on a focusable 
316                             //  element that element will be focused and become 
317                             //  the "activeElement".
318                             //  (http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx)
319                             //  However, there is a bug in IE where if there is 
320                             //  a positioned element with a focused descendant 
321                             //  that is hidden in response to the mousedown 
322                             //  event, the target of the mousedown event will 
323                             //  appear to have focus, but will not be set as 
324                             //  the activeElement.  This will result in the 
325                             //  element not firing key events, even though it
326                             //  appears to have focus.  The following call to 
327                             //  "setActive" fixes this bug.
328
329                             if (UA.ie && oTarget.focus && (UA.ie < 9)) {
330                                 oTarget.setActive();
331                             }
332         
333                         }
334                         else {
335                             
336                             if (oMenu.cfg.getProperty(_SHOW_DELAY) > 0) {
337                             
338                                 oMenu._cancelShowDelay();
339                             
340                             }
341
342
343                             if (oMenu.activeItem) {
344                         
345                                 oMenu.activeItem.blur();
346                                 oMenu.activeItem.cfg.setProperty(_SELECTED, false);
347                         
348                                 oMenu.activeItem = null;            
349                         
350                             }
351         
352                         }
353         
354                     }
355         
356                 } 
357     
358             }
359             
360         }
361     
362     
363         /**
364         * @method onMenuDestroy
365         * @description "destroy" event handler for a menu.
366         * @private
367         * @param {String} p_sType String representing the name of the event 
368         * that was fired.
369         * @param {Array} p_aArgs Array of arguments sent when the event 
370         * was fired.
371         * @param {YAHOO.widget.Menu} p_oMenu The menu that fired the event.
372         */
373         function onMenuDestroy(p_sType, p_aArgs, p_oMenu) {
374     
375             if (m_oMenus[p_oMenu.id]) {
376     
377                 this.removeMenu(p_oMenu);
378     
379             }
380     
381         }
382     
383     
384         /**
385         * @method onMenuFocus
386         * @description "focus" event handler for a MenuItem instance.
387         * @private
388         * @param {String} p_sType String representing the name of the event 
389         * that was fired.
390         * @param {Array} p_aArgs Array of arguments sent when the event 
391         * was fired.
392         */
393         function onMenuFocus(p_sType, p_aArgs) {
394     
395             var oItem = p_aArgs[1];
396     
397             if (oItem) {
398     
399                 m_oFocusedMenuItem = oItem;
400             
401             }
402     
403         }
404     
405     
406         /**
407         * @method onMenuBlur
408         * @description "blur" event handler for a MenuItem instance.
409         * @private
410         * @param {String} p_sType String representing the name of the event  
411         * that was fired.
412         * @param {Array} p_aArgs Array of arguments sent when the event 
413         * was fired.
414         */
415         function onMenuBlur(p_sType, p_aArgs) {
416     
417             m_oFocusedMenuItem = null;
418     
419         }
420
421     
422         /**
423         * @method onMenuVisibleConfigChange
424         * @description Event handler for when the "visible" configuration  
425         * property of a Menu instance changes.
426         * @private
427         * @param {String} p_sType String representing the name of the event  
428         * that was fired.
429         * @param {Array} p_aArgs Array of arguments sent when the event 
430         * was fired.
431         */
432         function onMenuVisibleConfigChange(p_sType, p_aArgs) {
433     
434             var bVisible = p_aArgs[0],
435                 sId = this.id;
436             
437             if (bVisible) {
438     
439                 m_oVisibleMenus[sId] = this;
440                 
441             
442             }
443             else if (m_oVisibleMenus[sId]) {
444             
445                 delete m_oVisibleMenus[sId];
446                 
447             
448             }
449         
450         }
451     
452     
453         /**
454         * @method onItemDestroy
455         * @description "destroy" event handler for a MenuItem instance.
456         * @private
457         * @param {String} p_sType String representing the name of the event  
458         * that was fired.
459         * @param {Array} p_aArgs Array of arguments sent when the event 
460         * was fired.
461         */
462         function onItemDestroy(p_sType, p_aArgs) {
463     
464             removeItem(this);
465     
466         }
467
468
469         /**
470         * @method removeItem
471         * @description Removes a MenuItem instance from the MenuManager's collection of MenuItems.
472         * @private
473         * @param {MenuItem} p_oMenuItem The MenuItem instance to be removed.
474         */    
475         function removeItem(p_oMenuItem) {
476
477             var sId = p_oMenuItem.id;
478     
479             if (sId && m_oItems[sId]) {
480     
481                 if (m_oFocusedMenuItem == p_oMenuItem) {
482     
483                     m_oFocusedMenuItem = null;
484     
485                 }
486     
487                 delete m_oItems[sId];
488                 
489                 p_oMenuItem.destroyEvent.unsubscribe(onItemDestroy);
490     
491     
492             }
493
494         }
495     
496     
497         /**
498         * @method onItemAdded
499         * @description "itemadded" event handler for a Menu instance.
500         * @private
501         * @param {String} p_sType String representing the name of the event  
502         * that was fired.
503         * @param {Array} p_aArgs Array of arguments sent when the event 
504         * was fired.
505         */
506         function onItemAdded(p_sType, p_aArgs) {
507     
508             var oItem = p_aArgs[0],
509                 sId;
510     
511             if (oItem instanceof YAHOO.widget.MenuItem) { 
512     
513                 sId = oItem.id;
514         
515                 if (!m_oItems[sId]) {
516             
517                     m_oItems[sId] = oItem;
518         
519                     oItem.destroyEvent.subscribe(onItemDestroy);
520         
521         
522                 }
523     
524             }
525         
526         }
527     
528     
529         return {
530     
531             // Privileged methods
532     
533     
534             /**
535             * @method addMenu
536             * @description Adds a menu to the collection of known menus.
537             * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu  
538             * instance to be added.
539             */
540             addMenu: function (p_oMenu) {
541     
542                 var oDoc;
543     
544                 if (p_oMenu instanceof YAHOO.widget.Menu && p_oMenu.id && 
545                     !m_oMenus[p_oMenu.id]) {
546         
547                     m_oMenus[p_oMenu.id] = p_oMenu;
548                 
549             
550                     if (!m_bInitializedEventHandlers) {
551             
552                         oDoc = document;
553                 
554                         Event.on(oDoc, _MOUSEOVER, onDOMEvent, this, true);
555                         Event.on(oDoc, _MOUSEOUT, onDOMEvent, this, true);
556                         Event.on(oDoc, _MOUSEDOWN, onDOMEvent, this, true);
557                         Event.on(oDoc, _MOUSEUP, onDOMEvent, this, true);
558                         Event.on(oDoc, _CLICK, onDOMEvent, this, true);
559                         Event.on(oDoc, _KEYDOWN, onDOMEvent, this, true);
560                         Event.on(oDoc, _KEYUP, onDOMEvent, this, true);
561                         Event.on(oDoc, _KEYPRESS, onDOMEvent, this, true);
562     
563                         Event.onFocus(oDoc, onDOMEvent, this, true);
564                         Event.onBlur(oDoc, onDOMEvent, this, true);                                             
565     
566                         m_bInitializedEventHandlers = true;
567                         
568             
569                     }
570             
571                     p_oMenu.cfg.subscribeToConfigEvent(_VISIBLE, onMenuVisibleConfigChange);
572                     p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this);
573                     p_oMenu.itemAddedEvent.subscribe(onItemAdded);
574                     p_oMenu.focusEvent.subscribe(onMenuFocus);
575                     p_oMenu.blurEvent.subscribe(onMenuBlur);
576         
577         
578                 }
579         
580             },
581     
582         
583             /**
584             * @method removeMenu
585             * @description Removes a menu from the collection of known menus.
586             * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu  
587             * instance to be removed.
588             */
589             removeMenu: function (p_oMenu) {
590     
591                 var sId,
592                     aItems,
593                     i;
594         
595                 if (p_oMenu) {
596     
597                     sId = p_oMenu.id;
598         
599                     if ((sId in m_oMenus) && (m_oMenus[sId] == p_oMenu)) {
600
601                         // Unregister each menu item
602
603                         aItems = p_oMenu.getItems();
604
605                         if (aItems && aItems.length > 0) {
606
607                             i = aItems.length - 1;
608
609                             do {
610
611                                 removeItem(aItems[i]);
612
613                             }
614                             while (i--);
615
616                         }
617
618
619                         // Unregister the menu
620
621                         delete m_oMenus[sId];
622             
623         
624
625                         /*
626                              Unregister the menu from the collection of 
627                              visible menus
628                         */
629
630                         if ((sId in m_oVisibleMenus) && (m_oVisibleMenus[sId] == p_oMenu)) {
631             
632                             delete m_oVisibleMenus[sId];
633                             
634        
635                         }
636
637
638                         // Unsubscribe event listeners
639
640                         if (p_oMenu.cfg) {
641
642                             p_oMenu.cfg.unsubscribeFromConfigEvent(_VISIBLE, 
643                                 onMenuVisibleConfigChange);
644                             
645                         }
646
647                         p_oMenu.destroyEvent.unsubscribe(onMenuDestroy, 
648                             p_oMenu);
649                 
650                         p_oMenu.itemAddedEvent.unsubscribe(onItemAdded);
651                         p_oMenu.focusEvent.unsubscribe(onMenuFocus);
652                         p_oMenu.blurEvent.unsubscribe(onMenuBlur);
653
654                     }
655                 
656                 }
657     
658             },
659         
660         
661             /**
662             * @method hideVisible
663             * @description Hides all visible, dynamically positioned menus 
664             * (excluding instances of YAHOO.widget.MenuBar).
665             */
666             hideVisible: function () {
667         
668                 var oMenu;
669         
670                 for (var i in m_oVisibleMenus) {
671         
672                     if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {
673         
674                         oMenu = m_oVisibleMenus[i];
675         
676                         if (!(oMenu instanceof YAHOO.widget.MenuBar) && 
677                             oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {
678         
679                             oMenu.hide();
680         
681                         }
682         
683                     }
684         
685                 }        
686     
687             },
688
689
690             /**
691             * @method getVisible
692             * @description Returns a collection of all visible menus registered
693             * with the menu manger.
694             * @return {Object}
695             */
696             getVisible: function () {
697             
698                 return m_oVisibleMenus;
699             
700             },
701
702     
703             /**
704             * @method getMenus
705             * @description Returns a collection of all menus registered with the 
706             * menu manger.
707             * @return {Object}
708             */
709             getMenus: function () {
710     
711                 return m_oMenus;
712             
713             },
714     
715     
716             /**
717             * @method getMenu
718             * @description Returns a menu with the specified id.
719             * @param {String} p_sId String specifying the id of the 
720             * <code>&#60;div&#62;</code> element representing the menu to
721             * be retrieved.
722             * @return {YAHOO.widget.Menu}
723             */
724             getMenu: function (p_sId) {
725                 
726                 var returnVal;
727                 
728                 if (p_sId in m_oMenus) {
729                 
730                     returnVal = m_oMenus[p_sId];
731                 
732                 }
733             
734                 return returnVal;
735             
736             },
737     
738     
739             /**
740             * @method getMenuItem
741             * @description Returns a menu item with the specified id.
742             * @param {String} p_sId String specifying the id of the 
743             * <code>&#60;li&#62;</code> element representing the menu item to
744             * be retrieved.
745             * @return {YAHOO.widget.MenuItem}
746             */
747             getMenuItem: function (p_sId) {
748     
749                 var returnVal;
750     
751                 if (p_sId in m_oItems) {
752     
753                     returnVal = m_oItems[p_sId];
754                 
755                 }
756                 
757                 return returnVal;
758             
759             },
760
761
762             /**
763             * @method getMenuItemGroup
764             * @description Returns an array of menu item instances whose 
765             * corresponding <code>&#60;li&#62;</code> elements are child 
766             * nodes of the <code>&#60;ul&#62;</code> element with the 
767             * specified id.
768             * @param {String} p_sId String specifying the id of the 
769             * <code>&#60;ul&#62;</code> element representing the group of 
770             * menu items to be retrieved.
771             * @return {Array}
772             */
773             getMenuItemGroup: function (p_sId) {
774
775                 var oUL = Dom.get(p_sId),
776                     aItems,
777                     oNode,
778                     oItem,
779                     sId,
780                     returnVal;
781     
782
783                 if (oUL && oUL.tagName && oUL.tagName.toUpperCase() == _UL) {
784
785                     oNode = oUL.firstChild;
786
787                     if (oNode) {
788
789                         aItems = [];
790                         
791                         do {
792
793                             sId = oNode.id;
794
795                             if (sId) {
796                             
797                                 oItem = this.getMenuItem(sId);
798                                 
799                                 if (oItem) {
800                                 
801                                     aItems[aItems.length] = oItem;
802                                 
803                                 }
804                             
805                             }
806                         
807                         }
808                         while ((oNode = oNode.nextSibling));
809
810
811                         if (aItems.length > 0) {
812
813                             returnVal = aItems;
814                         
815                         }
816
817                     }
818                 
819                 }
820
821                 return returnVal;
822             
823             },
824
825     
826             /**
827             * @method getFocusedMenuItem
828             * @description Returns a reference to the menu item that currently 
829             * has focus.
830             * @return {YAHOO.widget.MenuItem}
831             */
832             getFocusedMenuItem: function () {
833     
834                 return m_oFocusedMenuItem;
835     
836             },
837     
838     
839             /**
840             * @method getFocusedMenu
841             * @description Returns a reference to the menu that currently 
842             * has focus.
843             * @return {YAHOO.widget.Menu}
844             */
845             getFocusedMenu: function () {
846
847                 var returnVal;
848     
849                 if (m_oFocusedMenuItem) {
850     
851                     returnVal = m_oFocusedMenuItem.parent.getRoot();
852                 
853                 }
854     
855                 return returnVal;
856     
857             },
858     
859         
860             /**
861             * @method toString
862             * @description Returns a string representing the menu manager.
863             * @return {String}
864             */
865             toString: function () {
866             
867                 return _MENUMANAGER;
868             
869             }
870     
871         };
872     
873     }();
874
875 })();
876
877
878
879 (function () {
880
881     var Lang = YAHOO.lang,
882
883     // String constants
884     
885         _MENU = "Menu",
886         _DIV_UPPERCASE = "DIV",
887         _DIV_LOWERCASE = "div",
888         _ID = "id",
889         _SELECT = "SELECT",
890         _XY = "xy",
891         _Y = "y",
892         _UL_UPPERCASE = "UL",
893         _UL_LOWERCASE = "ul",
894         _FIRST_OF_TYPE = "first-of-type",
895         _LI = "LI",
896         _OPTGROUP = "OPTGROUP",
897         _OPTION = "OPTION",
898         _DISABLED = "disabled",
899         _NONE = "none",
900         _SELECTED = "selected",
901         _GROUP_INDEX = "groupindex",
902         _INDEX = "index",
903         _SUBMENU = "submenu",
904         _VISIBLE = "visible",
905         _HIDE_DELAY = "hidedelay",
906         _POSITION = "position",
907         _DYNAMIC = "dynamic",
908         _STATIC = "static",
909         _DYNAMIC_STATIC = _DYNAMIC + "," + _STATIC,
910         _URL = "url",
911         _HASH = "#",
912         _TARGET = "target",
913         _MAX_HEIGHT = "maxheight",
914         _TOP_SCROLLBAR = "topscrollbar",
915         _BOTTOM_SCROLLBAR = "bottomscrollbar",
916         _UNDERSCORE = "_",
917         _TOP_SCROLLBAR_DISABLED = _TOP_SCROLLBAR + _UNDERSCORE + _DISABLED,
918         _BOTTOM_SCROLLBAR_DISABLED = _BOTTOM_SCROLLBAR + _UNDERSCORE + _DISABLED,
919         _MOUSEMOVE = "mousemove",
920         _SHOW_DELAY = "showdelay",
921         _SUBMENU_HIDE_DELAY = "submenuhidedelay",
922         _IFRAME = "iframe",
923         _CONSTRAIN_TO_VIEWPORT = "constraintoviewport",
924         _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
925         _SUBMENU_ALIGNMENT = "submenualignment",
926         _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay",
927         _CLICK_TO_HIDE = "clicktohide",
928         _CONTAINER = "container",
929         _SCROLL_INCREMENT = "scrollincrement",
930         _MIN_SCROLL_HEIGHT = "minscrollheight",
931         _CLASSNAME = "classname",
932         _SHADOW = "shadow",
933         _KEEP_OPEN = "keepopen",
934         _HD = "hd",
935         _HAS_TITLE = "hastitle",
936         _CONTEXT = "context",
937         _EMPTY_STRING = "",
938         _MOUSEDOWN = "mousedown",
939         _KEYDOWN = "keydown",
940         _HEIGHT = "height",
941         _WIDTH = "width",
942         _PX = "px",
943         _EFFECT = "effect",
944         _MONITOR_RESIZE = "monitorresize",
945         _DISPLAY = "display",
946         _BLOCK = "block",
947         _VISIBILITY = "visibility",
948         _ABSOLUTE = "absolute",
949         _ZINDEX = "zindex",
950         _YUI_MENU_BODY_SCROLLED = "yui-menu-body-scrolled",
951         _NON_BREAKING_SPACE = "&#32;",
952         _SPACE = " ",
953         _MOUSEOVER = "mouseover",
954         _MOUSEOUT = "mouseout",
955         _ITEM_ADDED = "itemAdded",
956         _ITEM_REMOVED = "itemRemoved",
957         _HIDDEN = "hidden",
958         _YUI_MENU_SHADOW = "yui-menu-shadow",
959         _YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + "-visible",
960         _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + _SPACE + _YUI_MENU_SHADOW_VISIBLE;
961
962
963 /**
964 * The Menu class creates a container that holds a vertical list representing 
965 * a set of options or commands.  Menu is the base class for all 
966 * menu containers. 
967 * @param {String} p_oElement String specifying the id attribute of the 
968 * <code>&#60;div&#62;</code> element of the menu.
969 * @param {String} p_oElement String specifying the id attribute of the 
970 * <code>&#60;select&#62;</code> element to be used as the data source 
971 * for the menu.
972 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
973 * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 
974 * specifying the <code>&#60;div&#62;</code> element of the menu.
975 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
976 * level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement 
977 * Object specifying the <code>&#60;select&#62;</code> element to be used as 
978 * the data source for the menu.
979 * @param {Object} p_oConfig Optional. Object literal specifying the 
980 * configuration for the menu. See configuration class documentation for 
981 * more details.
982 * @namespace YAHOO.widget
983 * @class Menu
984 * @constructor
985 * @extends YAHOO.widget.Overlay
986 */
987 YAHOO.widget.Menu = function (p_oElement, p_oConfig) {
988
989     if (p_oConfig) {
990         this.parent = p_oConfig.parent;
991         this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload;
992         this.itemData = p_oConfig.itemData || p_oConfig.itemdata;
993     }
994
995     YAHOO.widget.Menu.superclass.constructor.call(this, p_oElement, p_oConfig);
996 };
997
998
999
1000 /**
1001 * @method checkPosition
1002 * @description Checks to make sure that the value of the "position" property 
1003 * is one of the supported strings. Returns true if the position is supported.
1004 * @private
1005 * @param {Object} p_sPosition String specifying the position of the menu.
1006 * @return {Boolean}
1007 */
1008 function checkPosition(p_sPosition) {
1009
1010     var returnVal = false;
1011
1012     if (Lang.isString(p_sPosition)) {
1013
1014         returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1);
1015
1016     }
1017
1018     return returnVal;
1019
1020 }
1021
1022
1023 var Dom = YAHOO.util.Dom,
1024     Event = YAHOO.util.Event,
1025     Module = YAHOO.widget.Module,
1026     Overlay = YAHOO.widget.Overlay,
1027     Menu = YAHOO.widget.Menu,
1028     MenuManager = YAHOO.widget.MenuManager,
1029     CustomEvent = YAHOO.util.CustomEvent,
1030     UA = YAHOO.env.ua,
1031     
1032     m_oShadowTemplate,
1033
1034     bFocusListenerInitialized = false,
1035
1036     oFocusedElement,
1037
1038     EVENT_TYPES = [
1039     
1040         ["mouseOverEvent", _MOUSEOVER],
1041         ["mouseOutEvent", _MOUSEOUT],
1042         ["mouseDownEvent", _MOUSEDOWN],
1043         ["mouseUpEvent", "mouseup"],
1044         ["clickEvent", "click"],
1045         ["keyPressEvent", "keypress"],
1046         ["keyDownEvent", _KEYDOWN],
1047         ["keyUpEvent", "keyup"],
1048         ["focusEvent", "focus"],
1049         ["blurEvent", "blur"],
1050         ["itemAddedEvent", _ITEM_ADDED],
1051         ["itemRemovedEvent", _ITEM_REMOVED]
1052
1053     ],
1054
1055     VISIBLE_CONFIG =  { 
1056         key: _VISIBLE, 
1057         value: false, 
1058         validator: Lang.isBoolean
1059     }, 
1060
1061     CONSTRAIN_TO_VIEWPORT_CONFIG =  {
1062         key: _CONSTRAIN_TO_VIEWPORT, 
1063         value: true, 
1064         validator: Lang.isBoolean, 
1065         supercedes: [_IFRAME,"x",_Y,_XY]
1066     }, 
1067
1068     PREVENT_CONTEXT_OVERLAP_CONFIG =  {
1069         key: _PREVENT_CONTEXT_OVERLAP,
1070         value: true,
1071         validator: Lang.isBoolean,  
1072         supercedes: [_CONSTRAIN_TO_VIEWPORT]
1073     },
1074
1075     POSITION_CONFIG =  { 
1076         key: _POSITION, 
1077         value: _DYNAMIC, 
1078         validator: checkPosition, 
1079         supercedes: [_VISIBLE, _IFRAME]
1080     }, 
1081
1082     SUBMENU_ALIGNMENT_CONFIG =  { 
1083         key: _SUBMENU_ALIGNMENT, 
1084         value: ["tl","tr"]
1085     },
1086
1087     AUTO_SUBMENU_DISPLAY_CONFIG =  { 
1088         key: _AUTO_SUBMENU_DISPLAY, 
1089         value: true, 
1090         validator: Lang.isBoolean,
1091         suppressEvent: true
1092     }, 
1093
1094     SHOW_DELAY_CONFIG =  { 
1095         key: _SHOW_DELAY, 
1096         value: 250, 
1097         validator: Lang.isNumber, 
1098         suppressEvent: true
1099     }, 
1100
1101     HIDE_DELAY_CONFIG =  { 
1102         key: _HIDE_DELAY, 
1103         value: 0, 
1104         validator: Lang.isNumber, 
1105         suppressEvent: true
1106     }, 
1107
1108     SUBMENU_HIDE_DELAY_CONFIG =  { 
1109         key: _SUBMENU_HIDE_DELAY, 
1110         value: 250, 
1111         validator: Lang.isNumber,
1112         suppressEvent: true
1113     }, 
1114
1115     CLICK_TO_HIDE_CONFIG =  { 
1116         key: _CLICK_TO_HIDE, 
1117         value: true, 
1118         validator: Lang.isBoolean,
1119         suppressEvent: true
1120     },
1121
1122     CONTAINER_CONFIG =  { 
1123         key: _CONTAINER,
1124         suppressEvent: true
1125     }, 
1126
1127     SCROLL_INCREMENT_CONFIG =  { 
1128         key: _SCROLL_INCREMENT, 
1129         value: 1, 
1130         validator: Lang.isNumber,
1131         supercedes: [_MAX_HEIGHT],
1132         suppressEvent: true
1133     },
1134
1135     MIN_SCROLL_HEIGHT_CONFIG =  { 
1136         key: _MIN_SCROLL_HEIGHT, 
1137         value: 90, 
1138         validator: Lang.isNumber,
1139         supercedes: [_MAX_HEIGHT],
1140         suppressEvent: true
1141     },    
1142
1143     MAX_HEIGHT_CONFIG =  { 
1144         key: _MAX_HEIGHT, 
1145         value: 0, 
1146         validator: Lang.isNumber,
1147         supercedes: [_IFRAME],
1148         suppressEvent: true
1149     }, 
1150
1151     CLASS_NAME_CONFIG =  { 
1152         key: _CLASSNAME, 
1153         value: null, 
1154         validator: Lang.isString,
1155         suppressEvent: true
1156     }, 
1157
1158     DISABLED_CONFIG =  { 
1159         key: _DISABLED, 
1160         value: false, 
1161         validator: Lang.isBoolean,
1162         suppressEvent: true
1163     },
1164     
1165     SHADOW_CONFIG =  { 
1166         key: _SHADOW, 
1167         value: true, 
1168         validator: Lang.isBoolean,
1169         suppressEvent: true,
1170         supercedes: [_VISIBLE]
1171     },
1172     
1173     KEEP_OPEN_CONFIG = {
1174         key: _KEEP_OPEN, 
1175         value: false, 
1176         validator: Lang.isBoolean
1177     };
1178
1179
1180 function onDocFocus(event) {
1181
1182     oFocusedElement = Event.getTarget(event);
1183
1184 }
1185
1186
1187
1188 YAHOO.lang.extend(Menu, Overlay, {
1189
1190
1191 // Constants
1192
1193
1194 /**
1195 * @property CSS_CLASS_NAME
1196 * @description String representing the CSS class(es) to be applied to the 
1197 * menu's <code>&#60;div&#62;</code> element.
1198 * @default "yuimenu"
1199 * @final
1200 * @type String
1201 */
1202 CSS_CLASS_NAME: "yuimenu",
1203
1204
1205 /**
1206 * @property ITEM_TYPE
1207 * @description Object representing the type of menu item to instantiate and 
1208 * add when parsing the child nodes (either <code>&#60;li&#62;</code> element, 
1209 * <code>&#60;optgroup&#62;</code> element or <code>&#60;option&#62;</code>) 
1210 * of the menu's source HTML element.
1211 * @default YAHOO.widget.MenuItem
1212 * @final
1213 * @type YAHOO.widget.MenuItem
1214 */
1215 ITEM_TYPE: null,
1216
1217
1218 /**
1219 * @property GROUP_TITLE_TAG_NAME
1220 * @description String representing the tagname of the HTML element used to 
1221 * title the menu's item groups.
1222 * @default H6
1223 * @final
1224 * @type String
1225 */
1226 GROUP_TITLE_TAG_NAME: "h6",
1227
1228
1229 /**
1230 * @property OFF_SCREEN_POSITION
1231 * @description Array representing the default x and y position that a menu 
1232 * should have when it is positioned outside the viewport by the 
1233 * "poistionOffScreen" method.
1234 * @default "-999em"
1235 * @final
1236 * @type String
1237 */
1238 OFF_SCREEN_POSITION: "-999em",
1239
1240
1241 // Private properties
1242
1243
1244 /** 
1245 * @property _useHideDelay
1246 * @description Boolean indicating if the "mouseover" and "mouseout" event 
1247 * handlers used for hiding the menu via a call to "YAHOO.lang.later" have 
1248 * already been assigned.
1249 * @default false
1250 * @private
1251 * @type Boolean
1252 */
1253 _useHideDelay: false,
1254
1255
1256 /**
1257 * @property _bHandledMouseOverEvent
1258 * @description Boolean indicating the current state of the menu's 
1259 * "mouseover" event.
1260 * @default false
1261 * @private
1262 * @type Boolean
1263 */
1264 _bHandledMouseOverEvent: false,
1265
1266
1267 /**
1268 * @property _bHandledMouseOutEvent
1269 * @description Boolean indicating the current state of the menu's
1270 * "mouseout" event.
1271 * @default false
1272 * @private
1273 * @type Boolean
1274 */
1275 _bHandledMouseOutEvent: false,
1276
1277
1278 /**
1279 * @property _aGroupTitleElements
1280 * @description Array of HTML element used to title groups of menu items.
1281 * @default []
1282 * @private
1283 * @type Array
1284 */
1285 _aGroupTitleElements: null,
1286
1287
1288 /**
1289 * @property _aItemGroups
1290 * @description Multi-dimensional Array representing the menu items as they
1291 * are grouped in the menu.
1292 * @default []
1293 * @private
1294 * @type Array
1295 */
1296 _aItemGroups: null,
1297
1298
1299 /**
1300 * @property _aListElements
1301 * @description Array of <code>&#60;ul&#62;</code> elements, each of which is 
1302 * the parent node for each item's <code>&#60;li&#62;</code> element.
1303 * @default []
1304 * @private
1305 * @type Array
1306 */
1307 _aListElements: null,
1308
1309
1310 /**
1311 * @property _nCurrentMouseX
1312 * @description The current x coordinate of the mouse inside the area of 
1313 * the menu.
1314 * @default 0
1315 * @private
1316 * @type Number
1317 */
1318 _nCurrentMouseX: 0,
1319
1320
1321 /**
1322 * @property _bStopMouseEventHandlers
1323 * @description Stops "mouseover," "mouseout," and "mousemove" event handlers 
1324 * from executing.
1325 * @default false
1326 * @private
1327 * @type Boolean
1328 */
1329 _bStopMouseEventHandlers: false,
1330
1331
1332 /**
1333 * @property _sClassName
1334 * @description The current value of the "classname" configuration attribute.
1335 * @default null
1336 * @private
1337 * @type String
1338 */
1339 _sClassName: null,
1340
1341
1342
1343 // Public properties
1344
1345
1346 /**
1347 * @property lazyLoad
1348 * @description Boolean indicating if the menu's "lazy load" feature is 
1349 * enabled.  If set to "true," initialization and rendering of the menu's 
1350 * items will be deferred until the first time it is made visible.  This 
1351 * property should be set via the constructor using the configuration 
1352 * object literal.
1353 * @default false
1354 * @type Boolean
1355 */
1356 lazyLoad: false,
1357
1358
1359 /**
1360 * @property itemData
1361 * @description Array of items to be added to the menu.  The array can contain 
1362 * strings representing the text for each item to be created, object literals 
1363 * representing the menu item configuration properties, or MenuItem instances.  
1364 * This property should be set via the constructor using the configuration 
1365 * object literal.
1366 * @default null
1367 * @type Array
1368 */
1369 itemData: null,
1370
1371
1372 /**
1373 * @property activeItem
1374 * @description Object reference to the item in the menu that has is selected.
1375 * @default null
1376 * @type YAHOO.widget.MenuItem
1377 */
1378 activeItem: null,
1379
1380
1381 /**
1382 * @property parent
1383 * @description Object reference to the menu's parent menu or menu item.  
1384 * This property can be set via the constructor using the configuration 
1385 * object literal.
1386 * @default null
1387 * @type YAHOO.widget.MenuItem
1388 */
1389 parent: null,
1390
1391
1392 /**
1393 * @property srcElement
1394 * @description Object reference to the HTML element (either 
1395 * <code>&#60;select&#62;</code> or <code>&#60;div&#62;</code>) used to 
1396 * create the menu.
1397 * @default null
1398 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
1399 * level-one-html.html#ID-94282980">HTMLSelectElement</a>|<a 
1400 * href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.
1401 * html#ID-22445964">HTMLDivElement</a>
1402 */
1403 srcElement: null,
1404
1405
1406
1407 // Events
1408
1409
1410 /**
1411 * @event mouseOverEvent
1412 * @description Fires when the mouse has entered the menu.  Passes back 
1413 * the DOM Event object as an argument.
1414 */
1415
1416
1417 /**
1418 * @event mouseOutEvent
1419 * @description Fires when the mouse has left the menu.  Passes back the DOM 
1420 * Event object as an argument.
1421 * @type YAHOO.util.CustomEvent
1422 */
1423
1424
1425 /**
1426 * @event mouseDownEvent
1427 * @description Fires when the user mouses down on the menu.  Passes back the 
1428 * DOM Event object as an argument.
1429 * @type YAHOO.util.CustomEvent
1430 */
1431
1432
1433 /**
1434 * @event mouseUpEvent
1435 * @description Fires when the user releases a mouse button while the mouse is 
1436 * over the menu.  Passes back the DOM Event object as an argument.
1437 * @type YAHOO.util.CustomEvent
1438 */
1439
1440
1441 /**
1442 * @event clickEvent
1443 * @description Fires when the user clicks the on the menu.  Passes back the 
1444 * DOM Event object as an argument.
1445 * @type YAHOO.util.CustomEvent
1446 */
1447
1448
1449 /**
1450 * @event keyPressEvent
1451 * @description Fires when the user presses an alphanumeric key when one of the
1452 * menu's items has focus.  Passes back the DOM Event object as an argument.
1453 * @type YAHOO.util.CustomEvent
1454 */
1455
1456
1457 /**
1458 * @event keyDownEvent
1459 * @description Fires when the user presses a key when one of the menu's items 
1460 * has focus.  Passes back the DOM Event object as an argument.
1461 * @type YAHOO.util.CustomEvent
1462 */
1463
1464
1465 /**
1466 * @event keyUpEvent
1467 * @description Fires when the user releases a key when one of the menu's items 
1468 * has focus.  Passes back the DOM Event object as an argument.
1469 * @type YAHOO.util.CustomEvent
1470 */
1471
1472
1473 /**
1474 * @event itemAddedEvent
1475 * @description Fires when an item is added to the menu.
1476 * @type YAHOO.util.CustomEvent
1477 */
1478
1479
1480 /**
1481 * @event itemRemovedEvent
1482 * @description Fires when an item is removed to the menu.
1483 * @type YAHOO.util.CustomEvent
1484 */
1485
1486
1487 /**
1488 * @method init
1489 * @description The Menu class's initialization method. This method is 
1490 * automatically called by the constructor, and sets up all DOM references 
1491 * for pre-existing markup, and creates required markup if it is not 
1492 * already present.
1493 * @param {String} p_oElement String specifying the id attribute of the 
1494 * <code>&#60;div&#62;</code> element of the menu.
1495 * @param {String} p_oElement String specifying the id attribute of the 
1496 * <code>&#60;select&#62;</code> element to be used as the data source 
1497 * for the menu.
1498 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
1499 * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 
1500 * specifying the <code>&#60;div&#62;</code> element of the menu.
1501 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
1502 * level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement 
1503 * Object specifying the <code>&#60;select&#62;</code> element to be used as 
1504 * the data source for the menu.
1505 * @param {Object} p_oConfig Optional. Object literal specifying the 
1506 * configuration for the menu. See configuration class documentation for 
1507 * more details.
1508 */
1509 init: function (p_oElement, p_oConfig) {
1510
1511     this._aItemGroups = [];
1512     this._aListElements = [];
1513     this._aGroupTitleElements = [];
1514
1515     if (!this.ITEM_TYPE) {
1516
1517         this.ITEM_TYPE = YAHOO.widget.MenuItem;
1518
1519     }
1520
1521
1522     var oElement;
1523
1524     if (Lang.isString(p_oElement)) {
1525
1526         oElement = Dom.get(p_oElement);
1527
1528     }
1529     else if (p_oElement.tagName) {
1530
1531         oElement = p_oElement;
1532
1533     }
1534
1535
1536     if (oElement && oElement.tagName) {
1537
1538         switch(oElement.tagName.toUpperCase()) {
1539     
1540             case _DIV_UPPERCASE:
1541
1542                 this.srcElement = oElement;
1543
1544                 if (!oElement.id) {
1545
1546                     oElement.setAttribute(_ID, Dom.generateId());
1547
1548                 }
1549
1550
1551                 /* 
1552                     Note: we don't pass the user config in here yet 
1553                     because we only want it executed once, at the lowest 
1554                     subclass level.
1555                 */ 
1556             
1557                 Menu.superclass.init.call(this, oElement);
1558
1559                 this.beforeInitEvent.fire(Menu);
1560
1561     
1562             break;
1563     
1564             case _SELECT:
1565     
1566                 this.srcElement = oElement;
1567
1568     
1569                 /*
1570                     The source element is not something that we can use 
1571                     outright, so we need to create a new Overlay
1572
1573                     Note: we don't pass the user config in here yet 
1574                     because we only want it executed once, at the lowest 
1575                     subclass level.
1576                 */ 
1577
1578                 Menu.superclass.init.call(this, Dom.generateId());
1579
1580                 this.beforeInitEvent.fire(Menu);
1581
1582
1583             break;
1584
1585         }
1586
1587     }
1588     else {
1589
1590         /* 
1591             Note: we don't pass the user config in here yet 
1592             because we only want it executed once, at the lowest 
1593             subclass level.
1594         */ 
1595     
1596         Menu.superclass.init.call(this, p_oElement);
1597
1598         this.beforeInitEvent.fire(Menu);
1599
1600
1601     }
1602
1603
1604     if (this.element) {
1605         Dom.addClass(this.element, this.CSS_CLASS_NAME);
1606
1607         // Subscribe to Custom Events
1608         this.initEvent.subscribe(this._onInit);
1609         this.beforeRenderEvent.subscribe(this._onBeforeRender);
1610         this.renderEvent.subscribe(this._onRender);
1611         this.beforeShowEvent.subscribe(this._onBeforeShow);
1612         this.hideEvent.subscribe(this._onHide);
1613         this.showEvent.subscribe(this._onShow);
1614         this.beforeHideEvent.subscribe(this._onBeforeHide);
1615         this.mouseOverEvent.subscribe(this._onMouseOver);
1616         this.mouseOutEvent.subscribe(this._onMouseOut);
1617         this.clickEvent.subscribe(this._onClick);
1618         this.keyDownEvent.subscribe(this._onKeyDown);
1619         this.keyPressEvent.subscribe(this._onKeyPress);
1620         this.blurEvent.subscribe(this._onBlur);
1621
1622         if (!bFocusListenerInitialized) {
1623             Event.onFocus(document, onDocFocus);
1624             bFocusListenerInitialized = true;
1625         }
1626
1627         //      Fixes an issue in Firefox 2 and Webkit where Dom's "getX" and "getY" 
1628         //      methods return values that don't take scrollTop into consideration 
1629
1630         if ((UA.gecko && UA.gecko < 1.9) || (UA.webkit && UA.webkit < 523)) {
1631             this.cfg.subscribeToConfigEvent(_Y, this._onYChange);
1632         }
1633
1634
1635         if (p_oConfig) {
1636             this.cfg.applyConfig(p_oConfig, true);
1637         }
1638
1639         // Register the Menu instance with the MenuManager
1640         MenuManager.addMenu(this);
1641
1642         this.initEvent.fire(Menu);
1643     }
1644 },
1645
1646
1647
1648 // Private methods
1649
1650
1651 /**
1652 * @method _initSubTree
1653 * @description Iterates the childNodes of the source element to find nodes 
1654 * used to instantiate menu and menu items.
1655 * @private
1656 */
1657 _initSubTree: function () {
1658
1659     var oSrcElement = this.srcElement,
1660         sSrcElementTagName,
1661         nGroup,
1662         sGroupTitleTagName,
1663         oNode,
1664         aListElements,
1665         nListElements,
1666         i;
1667
1668
1669     if (oSrcElement) {
1670     
1671         sSrcElementTagName = 
1672             (oSrcElement.tagName && oSrcElement.tagName.toUpperCase());
1673
1674
1675         if (sSrcElementTagName == _DIV_UPPERCASE) {
1676     
1677             //  Populate the collection of item groups and item group titles
1678     
1679             oNode = this.body.firstChild;
1680     
1681
1682             if (oNode) {
1683     
1684                 nGroup = 0;
1685                 sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase();
1686         
1687                 do {
1688         
1689
1690                     if (oNode && oNode.tagName) {
1691         
1692                         switch (oNode.tagName.toUpperCase()) {
1693         
1694                             case sGroupTitleTagName:
1695                             
1696                                 this._aGroupTitleElements[nGroup] = oNode;
1697         
1698                             break;
1699         
1700                             case _UL_UPPERCASE:
1701         
1702                                 this._aListElements[nGroup] = oNode;
1703                                 this._aItemGroups[nGroup] = [];
1704                                 nGroup++;
1705         
1706                             break;
1707         
1708                         }
1709                     
1710                     }
1711         
1712                 }
1713                 while ((oNode = oNode.nextSibling));
1714         
1715         
1716                 /*
1717                     Apply the "first-of-type" class to the first UL to mimic 
1718                     the ":first-of-type" CSS3 psuedo class.
1719                 */
1720         
1721                 if (this._aListElements[0]) {
1722         
1723                     Dom.addClass(this._aListElements[0], _FIRST_OF_TYPE);
1724         
1725                 }
1726             
1727             }
1728     
1729         }
1730     
1731     
1732         oNode = null;
1733     
1734     
1735
1736         if (sSrcElementTagName) {
1737     
1738             switch (sSrcElementTagName) {
1739         
1740                 case _DIV_UPPERCASE:
1741
1742                     aListElements = this._aListElements;
1743                     nListElements = aListElements.length;
1744         
1745                     if (nListElements > 0) {
1746         
1747         
1748                         i = nListElements - 1;
1749         
1750                         do {
1751         
1752                             oNode = aListElements[i].firstChild;
1753             
1754                             if (oNode) {
1755
1756             
1757                                 do {
1758                 
1759                                     if (oNode && oNode.tagName && 
1760                                         oNode.tagName.toUpperCase() == _LI) {
1761                 
1762         
1763                                         this.addItem(new this.ITEM_TYPE(oNode, 
1764                                                     { parent: this }), i);
1765             
1766                                     }
1767                         
1768                                 }
1769                                 while ((oNode = oNode.nextSibling));
1770                             
1771                             }
1772                     
1773                         }
1774                         while (i--);
1775         
1776                     }
1777         
1778                 break;
1779         
1780                 case _SELECT:
1781         
1782         
1783                     oNode = oSrcElement.firstChild;
1784         
1785                     do {
1786         
1787                         if (oNode && oNode.tagName) {
1788                         
1789                             switch (oNode.tagName.toUpperCase()) {
1790             
1791                                 case _OPTGROUP:
1792                                 case _OPTION:
1793             
1794             
1795                                     this.addItem(
1796                                             new this.ITEM_TYPE(
1797                                                     oNode, 
1798                                                     { parent: this }
1799                                                 )
1800                                             );
1801             
1802                                 break;
1803             
1804                             }
1805     
1806                         }
1807         
1808                     }
1809                     while ((oNode = oNode.nextSibling));
1810         
1811                 break;
1812         
1813             }
1814     
1815         }    
1816     
1817     }
1818
1819 },
1820
1821
1822 /**
1823 * @method _getFirstEnabledItem
1824 * @description Returns the first enabled item in the menu.
1825 * @return {YAHOO.widget.MenuItem}
1826 * @private
1827 */
1828 _getFirstEnabledItem: function () {
1829
1830     var aItems = this.getItems(),
1831         nItems = aItems.length,
1832         oItem,
1833         returnVal;
1834     
1835
1836     for(var i=0; i<nItems; i++) {
1837
1838         oItem = aItems[i];
1839
1840         if (oItem && !oItem.cfg.getProperty(_DISABLED) && oItem.element.style.display != _NONE) {
1841
1842             returnVal = oItem;
1843             break;
1844
1845         }
1846     
1847     }
1848     
1849     return returnVal;
1850     
1851 },
1852
1853
1854 /**
1855 * @method _addItemToGroup
1856 * @description Adds a menu item to a group.
1857 * @private
1858 * @param {Number} p_nGroupIndex Number indicating the group to which the 
1859 * item belongs.
1860 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
1861 * instance to be added to the menu.
1862 * @param {HTML} p_oItem String or markup specifying the content of the item to be added 
1863 * to the menu. The item is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
1864 * @param {Object} p_oItem Object literal containing a set of menu item 
1865 * configuration properties.
1866 * @param {Number} p_nItemIndex Optional. Number indicating the index at 
1867 * which the menu item should be added.
1868 * @return {YAHOO.widget.MenuItem}
1869 */
1870 _addItemToGroup: function (p_nGroupIndex, p_oItem, p_nItemIndex) {
1871
1872     var oItem,
1873         nGroupIndex,
1874         aGroup,
1875         oGroupItem,
1876         bAppend,
1877         oNextItemSibling,
1878         nItemIndex,
1879         returnVal;
1880
1881
1882     function getNextItemSibling(p_aArray, p_nStartIndex) {
1883
1884         return (p_aArray[p_nStartIndex] || getNextItemSibling(p_aArray, (p_nStartIndex+1)));
1885
1886     }
1887
1888
1889     if (p_oItem instanceof this.ITEM_TYPE) {
1890
1891         oItem = p_oItem;
1892         oItem.parent = this;
1893
1894     }
1895     else if (Lang.isString(p_oItem)) {
1896
1897         oItem = new this.ITEM_TYPE(p_oItem, { parent: this });
1898     
1899     }
1900     else if (Lang.isObject(p_oItem)) {
1901
1902         p_oItem.parent = this;
1903
1904         oItem = new this.ITEM_TYPE(p_oItem.text, p_oItem);
1905
1906     }
1907
1908
1909     if (oItem) {
1910
1911         if (oItem.cfg.getProperty(_SELECTED)) {
1912
1913             this.activeItem = oItem;
1914         
1915         }
1916
1917
1918         nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0;
1919         aGroup = this._getItemGroup(nGroupIndex);
1920
1921
1922
1923         if (!aGroup) {
1924
1925             aGroup = this._createItemGroup(nGroupIndex);
1926
1927         }
1928
1929
1930         if (Lang.isNumber(p_nItemIndex)) {
1931
1932             bAppend = (p_nItemIndex >= aGroup.length);            
1933
1934
1935             if (aGroup[p_nItemIndex]) {
1936     
1937                 aGroup.splice(p_nItemIndex, 0, oItem);
1938     
1939             }
1940             else {
1941     
1942                 aGroup[p_nItemIndex] = oItem;
1943     
1944             }
1945
1946
1947             oGroupItem = aGroup[p_nItemIndex];
1948
1949             if (oGroupItem) {
1950
1951                 if (bAppend && (!oGroupItem.element.parentNode || 
1952                         oGroupItem.element.parentNode.nodeType == 11)) {
1953         
1954                     this._aListElements[nGroupIndex].appendChild(oGroupItem.element);
1955     
1956                 }
1957                 else {
1958     
1959                     oNextItemSibling = getNextItemSibling(aGroup, (p_nItemIndex+1));
1960     
1961                     if (oNextItemSibling && (!oGroupItem.element.parentNode || 
1962                             oGroupItem.element.parentNode.nodeType == 11)) {
1963             
1964                         this._aListElements[nGroupIndex].insertBefore(
1965                                 oGroupItem.element, oNextItemSibling.element);
1966         
1967                     }
1968     
1969                 }
1970     
1971
1972                 oGroupItem.parent = this;
1973         
1974                 this._subscribeToItemEvents(oGroupItem);
1975     
1976                 this._configureSubmenu(oGroupItem);
1977                 
1978                 this._updateItemProperties(nGroupIndex);
1979         
1980
1981                 this.itemAddedEvent.fire(oGroupItem);
1982                 this.changeContentEvent.fire();
1983
1984                 returnVal = oGroupItem;
1985     
1986             }
1987
1988         }
1989         else {
1990     
1991             nItemIndex = aGroup.length;
1992     
1993             aGroup[nItemIndex] = oItem;
1994
1995             oGroupItem = aGroup[nItemIndex];
1996     
1997
1998             if (oGroupItem) {
1999     
2000                 if (!Dom.isAncestor(this._aListElements[nGroupIndex], oGroupItem.element)) {
2001     
2002                     this._aListElements[nGroupIndex].appendChild(oGroupItem.element);
2003     
2004                 }
2005     
2006                 oGroupItem.element.setAttribute(_GROUP_INDEX, nGroupIndex);
2007                 oGroupItem.element.setAttribute(_INDEX, nItemIndex);
2008         
2009                 oGroupItem.parent = this;
2010     
2011                 oGroupItem.index = nItemIndex;
2012                 oGroupItem.groupIndex = nGroupIndex;
2013         
2014                 this._subscribeToItemEvents(oGroupItem);
2015     
2016                 this._configureSubmenu(oGroupItem);
2017     
2018                 if (nItemIndex === 0) {
2019         
2020                     Dom.addClass(oGroupItem.element, _FIRST_OF_TYPE);
2021         
2022                 }
2023
2024         
2025
2026                 this.itemAddedEvent.fire(oGroupItem);
2027                 this.changeContentEvent.fire();
2028
2029                 returnVal = oGroupItem;
2030     
2031             }
2032     
2033         }
2034
2035     }
2036     
2037     return returnVal;
2038     
2039 },
2040
2041
2042 /**
2043 * @method _removeItemFromGroupByIndex
2044 * @description Removes a menu item from a group by index.  Returns the menu 
2045 * item that was removed.
2046 * @private
2047 * @param {Number} p_nGroupIndex Number indicating the group to which the menu 
2048 * item belongs.
2049 * @param {Number} p_nItemIndex Number indicating the index of the menu item 
2050 * to be removed.
2051 * @return {YAHOO.widget.MenuItem}
2052 */
2053 _removeItemFromGroupByIndex: function (p_nGroupIndex, p_nItemIndex) {
2054
2055     var nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0,
2056         aGroup = this._getItemGroup(nGroupIndex),
2057         aArray,
2058         oItem,
2059         oUL;
2060
2061     if (aGroup) {
2062
2063         aArray = aGroup.splice(p_nItemIndex, 1);
2064         oItem = aArray[0];
2065     
2066         if (oItem) {
2067     
2068             // Update the index and className properties of each member        
2069             
2070             this._updateItemProperties(nGroupIndex);
2071     
2072             if (aGroup.length === 0) {
2073     
2074                 // Remove the UL
2075     
2076                 oUL = this._aListElements[nGroupIndex];
2077     
2078                 if (oUL && oUL.parentNode) {
2079                     oUL.parentNode.removeChild(oUL);
2080                 }
2081     
2082                 // Remove the group from the array of items
2083     
2084                 this._aItemGroups.splice(nGroupIndex, 1);
2085     
2086     
2087                 // Remove the UL from the array of ULs
2088     
2089                 this._aListElements.splice(nGroupIndex, 1);
2090     
2091     
2092                 /*
2093                      Assign the "first-of-type" class to the new first UL 
2094                      in the collection
2095                 */
2096     
2097                 oUL = this._aListElements[0];
2098     
2099                 if (oUL) {
2100     
2101                     Dom.addClass(oUL, _FIRST_OF_TYPE);
2102     
2103                 }            
2104     
2105             }
2106     
2107
2108             this.itemRemovedEvent.fire(oItem);
2109             this.changeContentEvent.fire();
2110     
2111         }
2112
2113     }
2114
2115     // Return a reference to the item that was removed
2116
2117     return oItem;
2118     
2119 },
2120
2121
2122 /**
2123 * @method _removeItemFromGroupByValue
2124 * @description Removes a menu item from a group by reference.  Returns the 
2125 * menu item that was removed.
2126 * @private
2127 * @param {Number} p_nGroupIndex Number indicating the group to which the
2128 * menu item belongs.
2129 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
2130 * instance to be removed.
2131 * @return {YAHOO.widget.MenuItem}
2132 */    
2133 _removeItemFromGroupByValue: function (p_nGroupIndex, p_oItem) {
2134
2135     var aGroup = this._getItemGroup(p_nGroupIndex),
2136         nItems,
2137         nItemIndex,
2138         returnVal,
2139         i;
2140
2141     if (aGroup) {
2142
2143         nItems = aGroup.length;
2144         nItemIndex = -1;
2145     
2146         if (nItems > 0) {
2147     
2148             i = nItems-1;
2149         
2150             do {
2151         
2152                 if (aGroup[i] == p_oItem) {
2153         
2154                     nItemIndex = i;
2155                     break;    
2156         
2157                 }
2158         
2159             }
2160             while (i--);
2161         
2162             if (nItemIndex > -1) {
2163         
2164                 returnVal = this._removeItemFromGroupByIndex(p_nGroupIndex, nItemIndex);
2165         
2166             }
2167     
2168         }
2169     
2170     }
2171     
2172     return returnVal;
2173
2174 },
2175
2176
2177 /**
2178 * @method _updateItemProperties
2179 * @description Updates the "index," "groupindex," and "className" properties 
2180 * of the menu items in the specified group. 
2181 * @private
2182 * @param {Number} p_nGroupIndex Number indicating the group of items to update.
2183 */
2184 _updateItemProperties: function (p_nGroupIndex) {
2185
2186     var aGroup = this._getItemGroup(p_nGroupIndex),
2187         nItems = aGroup.length,
2188         oItem,
2189         oLI,
2190         i;
2191
2192
2193     if (nItems > 0) {
2194
2195         i = nItems - 1;
2196
2197         // Update the index and className properties of each member
2198     
2199         do {
2200
2201             oItem = aGroup[i];
2202
2203             if (oItem) {
2204     
2205                 oLI = oItem.element;
2206
2207                 oItem.index = i;
2208                 oItem.groupIndex = p_nGroupIndex;
2209
2210                 oLI.setAttribute(_GROUP_INDEX, p_nGroupIndex);
2211                 oLI.setAttribute(_INDEX, i);
2212
2213                 Dom.removeClass(oLI, _FIRST_OF_TYPE);
2214
2215             }
2216     
2217         }
2218         while (i--);
2219
2220
2221         if (oLI) {
2222
2223             Dom.addClass(oLI, _FIRST_OF_TYPE);
2224
2225         }
2226
2227     }
2228
2229 },
2230
2231
2232 /**
2233 * @method _createItemGroup
2234 * @description Creates a new menu item group (array) and its associated 
2235 * <code>&#60;ul&#62;</code> element. Returns an aray of menu item groups.
2236 * @private
2237 * @param {Number} p_nIndex Number indicating the group to create.
2238 * @return {Array}
2239 */
2240 _createItemGroup: function (p_nIndex) {
2241
2242     var oUL,
2243         returnVal;
2244
2245     if (!this._aItemGroups[p_nIndex]) {
2246
2247         this._aItemGroups[p_nIndex] = [];
2248
2249         oUL = document.createElement(_UL_LOWERCASE);
2250
2251         this._aListElements[p_nIndex] = oUL;
2252
2253         returnVal = this._aItemGroups[p_nIndex];
2254
2255     }
2256     
2257     return returnVal;
2258
2259 },
2260
2261
2262 /**
2263 * @method _getItemGroup
2264 * @description Returns the menu item group at the specified index.
2265 * @private
2266 * @param {Number} p_nIndex Number indicating the index of the menu item group 
2267 * to be retrieved.
2268 * @return {Array}
2269 */
2270 _getItemGroup: function (p_nIndex) {
2271
2272     var nIndex = Lang.isNumber(p_nIndex) ? p_nIndex : 0,
2273         aGroups = this._aItemGroups,
2274         returnVal;
2275
2276     if (nIndex in aGroups) {
2277
2278         returnVal = aGroups[nIndex];
2279
2280     }
2281     
2282     return returnVal;
2283
2284 },
2285
2286
2287 /**
2288 * @method _configureSubmenu
2289 * @description Subscribes the menu item's submenu to its parent menu's events.
2290 * @private
2291 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
2292 * instance with the submenu to be configured.
2293 */
2294 _configureSubmenu: function (p_oItem) {
2295
2296     var oSubmenu = p_oItem.cfg.getProperty(_SUBMENU);
2297
2298     if (oSubmenu) {
2299             
2300         /*
2301             Listen for configuration changes to the parent menu 
2302             so they they can be applied to the submenu.
2303         */
2304
2305         this.cfg.configChangedEvent.subscribe(this._onParentMenuConfigChange, oSubmenu, true);
2306
2307         this.renderEvent.subscribe(this._onParentMenuRender, oSubmenu, true);
2308
2309     }
2310
2311 },
2312
2313
2314
2315
2316 /**
2317 * @method _subscribeToItemEvents
2318 * @description Subscribes a menu to a menu item's event.
2319 * @private
2320 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
2321 * instance whose events should be subscribed to.
2322 */
2323 _subscribeToItemEvents: function (p_oItem) {
2324
2325     p_oItem.destroyEvent.subscribe(this._onMenuItemDestroy, p_oItem, this);
2326     p_oItem.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange, p_oItem, this);
2327
2328 },
2329
2330
2331 /**
2332 * @method _onVisibleChange
2333 * @description Change event handler for the the menu's "visible" configuration
2334 * property.
2335 * @private
2336 * @param {String} p_sType String representing the name of the event that 
2337 * was fired.
2338 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
2339 */
2340 _onVisibleChange: function (p_sType, p_aArgs) {
2341
2342     var bVisible = p_aArgs[0];
2343     
2344     if (bVisible) {
2345
2346         Dom.addClass(this.element, _VISIBLE);
2347
2348     }
2349     else {
2350
2351         Dom.removeClass(this.element, _VISIBLE);
2352
2353     }
2354
2355 },
2356
2357
2358 /**
2359 * @method _cancelHideDelay
2360 * @description Cancels the call to "hideMenu."
2361 * @private
2362 */
2363 _cancelHideDelay: function () {
2364
2365     var oTimer = this.getRoot()._hideDelayTimer;
2366
2367     if (oTimer) {
2368
2369         oTimer.cancel();
2370
2371     }
2372
2373 },
2374
2375
2376 /**
2377 * @method _execHideDelay
2378 * @description Hides the menu after the number of milliseconds specified by 
2379 * the "hidedelay" configuration property.
2380 * @private
2381 */
2382 _execHideDelay: function () {
2383
2384     this._cancelHideDelay();
2385
2386     var oRoot = this.getRoot();
2387
2388     oRoot._hideDelayTimer = Lang.later(oRoot.cfg.getProperty(_HIDE_DELAY), this, function () {
2389     
2390         if (oRoot.activeItem) {
2391             if (oRoot.hasFocus()) {
2392                 oRoot.activeItem.focus();
2393             }
2394             oRoot.clearActiveItem();
2395         }
2396
2397         if (oRoot == this && !(this instanceof YAHOO.widget.MenuBar) && 
2398             this.cfg.getProperty(_POSITION) == _DYNAMIC) {
2399             this.hide();
2400         }
2401     });
2402
2403 },
2404
2405
2406 /**
2407 * @method _cancelShowDelay
2408 * @description Cancels the call to the "showMenu."
2409 * @private
2410 */
2411 _cancelShowDelay: function () {
2412     var oTimer = this.getRoot()._showDelayTimer;
2413     if (oTimer) {
2414         oTimer.cancel();
2415     }
2416 },
2417
2418
2419 /**
2420 * @method _execSubmenuHideDelay
2421 * @description Hides a submenu after the number of milliseconds specified by 
2422 * the "submenuhidedelay" configuration property have elapsed.
2423 * @private
2424 * @param {YAHOO.widget.Menu} p_oSubmenu Object specifying the submenu that  
2425 * should be hidden.
2426 * @param {Number} p_nMouseX The x coordinate of the mouse when it left 
2427 * the specified submenu's parent menu item.
2428 * @param {Number} p_nHideDelay The number of milliseconds that should ellapse
2429 * before the submenu is hidden.
2430 */
2431 _execSubmenuHideDelay: function (p_oSubmenu, p_nMouseX, p_nHideDelay) {
2432
2433     p_oSubmenu._submenuHideDelayTimer = Lang.later(50, this, function () {
2434
2435         if (this._nCurrentMouseX > (p_nMouseX + 10)) {
2436
2437             p_oSubmenu._submenuHideDelayTimer = Lang.later(p_nHideDelay, p_oSubmenu, function () {
2438         
2439                 this.hide();
2440
2441             });
2442
2443         }
2444         else {
2445
2446             p_oSubmenu.hide();
2447         
2448         }
2449     
2450     });
2451
2452 },
2453
2454
2455
2456 // Protected methods
2457
2458
2459 /**
2460 * @method _disableScrollHeader
2461 * @description Disables the header used for scrolling the body of the menu.
2462 * @protected
2463 */
2464 _disableScrollHeader: function () {
2465
2466     if (!this._bHeaderDisabled) {
2467
2468         Dom.addClass(this.header, _TOP_SCROLLBAR_DISABLED);
2469         this._bHeaderDisabled = true;
2470
2471     }
2472
2473 },
2474
2475
2476 /**
2477 * @method _disableScrollFooter
2478 * @description Disables the footer used for scrolling the body of the menu.
2479 * @protected
2480 */
2481 _disableScrollFooter: function () {
2482
2483     if (!this._bFooterDisabled) {
2484
2485         Dom.addClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED);
2486         this._bFooterDisabled = true;
2487
2488     }
2489
2490 },
2491
2492
2493 /**
2494 * @method _enableScrollHeader
2495 * @description Enables the header used for scrolling the body of the menu.
2496 * @protected
2497 */
2498 _enableScrollHeader: function () {
2499
2500     if (this._bHeaderDisabled) {
2501
2502         Dom.removeClass(this.header, _TOP_SCROLLBAR_DISABLED);
2503         this._bHeaderDisabled = false;
2504
2505     }
2506
2507 },
2508
2509
2510 /**
2511 * @method _enableScrollFooter
2512 * @description Enables the footer used for scrolling the body of the menu.
2513 * @protected
2514 */
2515 _enableScrollFooter: function () {
2516
2517     if (this._bFooterDisabled) {
2518
2519         Dom.removeClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED);
2520         this._bFooterDisabled = false;
2521
2522     }
2523
2524 },
2525
2526
2527 /**
2528 * @method _onMouseOver
2529 * @description "mouseover" event handler for the menu.
2530 * @protected
2531 * @param {String} p_sType String representing the name of the event that 
2532 * was fired.
2533 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
2534 */
2535 _onMouseOver: function (p_sType, p_aArgs) {
2536
2537     var oEvent = p_aArgs[0],
2538         oItem = p_aArgs[1],
2539         oTarget = Event.getTarget(oEvent),
2540         oRoot = this.getRoot(),
2541         oSubmenuHideDelayTimer = this._submenuHideDelayTimer,
2542         oParentMenu,
2543         nShowDelay,
2544         bShowDelay,
2545         oActiveItem,
2546         oItemCfg,
2547         oSubmenu;
2548
2549
2550     var showSubmenu = function () {
2551
2552         if (this.parent.cfg.getProperty(_SELECTED)) {
2553
2554             this.show();
2555
2556         }
2557
2558     };
2559
2560
2561     if (!this._bStopMouseEventHandlers) {
2562     
2563         if (!this._bHandledMouseOverEvent && (oTarget == this.element || 
2564                 Dom.isAncestor(this.element, oTarget))) {
2565     
2566             // Menu mouseover logic
2567
2568             if (this._useHideDelay) {
2569                 this._cancelHideDelay();
2570             }
2571     
2572             this._nCurrentMouseX = 0;
2573     
2574             Event.on(this.element, _MOUSEMOVE, this._onMouseMove, this, true);
2575
2576
2577             /*
2578                 If the mouse is moving from the submenu back to its corresponding menu item, 
2579                 don't hide the submenu or clear the active MenuItem.
2580             */
2581
2582             if (!(oItem && Dom.isAncestor(oItem.element, Event.getRelatedTarget(oEvent)))) {
2583
2584                 this.clearActiveItem();
2585
2586             }
2587     
2588
2589             if (this.parent && oSubmenuHideDelayTimer) {
2590     
2591                 oSubmenuHideDelayTimer.cancel();
2592     
2593                 this.parent.cfg.setProperty(_SELECTED, true);
2594     
2595                 oParentMenu = this.parent.parent;
2596     
2597                 oParentMenu._bHandledMouseOutEvent = true;
2598                 oParentMenu._bHandledMouseOverEvent = false;
2599     
2600             }
2601     
2602     
2603             this._bHandledMouseOverEvent = true;
2604             this._bHandledMouseOutEvent = false;
2605         
2606         }
2607     
2608     
2609         if (oItem && !oItem.handledMouseOverEvent && !oItem.cfg.getProperty(_DISABLED) && 
2610             (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))) {
2611     
2612             // Menu Item mouseover logic
2613     
2614             nShowDelay = this.cfg.getProperty(_SHOW_DELAY);
2615             bShowDelay = (nShowDelay > 0);
2616     
2617     
2618             if (bShowDelay) {
2619             
2620                 this._cancelShowDelay();
2621             
2622             }
2623     
2624     
2625             oActiveItem = this.activeItem;
2626         
2627             if (oActiveItem) {
2628         
2629                 oActiveItem.cfg.setProperty(_SELECTED, false);
2630         
2631             }
2632     
2633     
2634             oItemCfg = oItem.cfg;
2635         
2636             // Select and focus the current menu item
2637         
2638             oItemCfg.setProperty(_SELECTED, true);
2639     
2640     
2641             if (this.hasFocus() || oRoot._hasFocus) {
2642             
2643                 oItem.focus();
2644                 
2645                 oRoot._hasFocus = false;
2646             
2647             }
2648     
2649     
2650             if (this.cfg.getProperty(_AUTO_SUBMENU_DISPLAY)) {
2651     
2652                 // Show the submenu this menu item
2653     
2654                 oSubmenu = oItemCfg.getProperty(_SUBMENU);
2655             
2656                 if (oSubmenu) {
2657             
2658                     if (bShowDelay) {
2659     
2660                         oRoot._showDelayTimer = 
2661                             Lang.later(oRoot.cfg.getProperty(_SHOW_DELAY), oSubmenu, showSubmenu);
2662             
2663                     }
2664                     else {
2665     
2666                         oSubmenu.show();
2667     
2668                     }
2669     
2670                 }
2671     
2672             }                        
2673     
2674             oItem.handledMouseOverEvent = true;
2675             oItem.handledMouseOutEvent = false;
2676     
2677         }
2678     
2679     }
2680
2681 },
2682
2683
2684 /**
2685 * @method _onMouseOut
2686 * @description "mouseout" event handler for the menu.
2687 * @protected
2688 * @param {String} p_sType String representing the name of the event that 
2689 * was fired.
2690 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
2691 */
2692 _onMouseOut: function (p_sType, p_aArgs) {
2693
2694     var oEvent = p_aArgs[0],
2695         oItem = p_aArgs[1],
2696         oRelatedTarget = Event.getRelatedTarget(oEvent),
2697         bMovingToSubmenu = false,
2698         oItemCfg,
2699         oSubmenu,
2700         nSubmenuHideDelay,
2701         nShowDelay;
2702
2703
2704
2705     if (!this._bStopMouseEventHandlers) {
2706     
2707         if (oItem && !oItem.cfg.getProperty(_DISABLED)) {
2708     
2709             oItemCfg = oItem.cfg;
2710             oSubmenu = oItemCfg.getProperty(_SUBMENU);
2711     
2712     
2713             if (oSubmenu && (oRelatedTarget == oSubmenu.element || Dom.isAncestor(oSubmenu.element, oRelatedTarget))) {
2714                 bMovingToSubmenu = true;
2715             }
2716     
2717             if (!oItem.handledMouseOutEvent && ((oRelatedTarget != oItem.element && !Dom.isAncestor(oItem.element, oRelatedTarget)) || bMovingToSubmenu)) {
2718                 if (!bMovingToSubmenu) {
2719                     oItem.cfg.setProperty(_SELECTED, false);
2720                     if (oSubmenu) {
2721                         
2722                         nSubmenuHideDelay = this.cfg.getProperty(_SUBMENU_HIDE_DELAY);
2723                         nShowDelay = this.cfg.getProperty(_SHOW_DELAY);
2724                         if (!(this instanceof YAHOO.widget.MenuBar) && nSubmenuHideDelay > 0 && nSubmenuHideDelay >= nShowDelay) {
2725                             this._execSubmenuHideDelay(oSubmenu, Event.getPageX(oEvent), nSubmenuHideDelay);
2726                         } else {
2727                             oSubmenu.hide();
2728                         }
2729                     }
2730                 }
2731     
2732                 oItem.handledMouseOutEvent = true;
2733                 oItem.handledMouseOverEvent = false;
2734             }
2735         }
2736
2737
2738         if (!this._bHandledMouseOutEvent) {
2739             if (this._didMouseLeave(oRelatedTarget) || bMovingToSubmenu) {
2740                 // Menu mouseout logic
2741                 if (this._useHideDelay) {
2742                     this._execHideDelay();
2743                 }
2744     
2745                 Event.removeListener(this.element, _MOUSEMOVE, this._onMouseMove);
2746         
2747                 this._nCurrentMouseX = Event.getPageX(oEvent);
2748         
2749                 this._bHandledMouseOutEvent = true;
2750                 this._bHandledMouseOverEvent = false;
2751             }
2752         }
2753     }
2754
2755 },
2756
2757 /**
2758  * Utilility method to determine if we really moused out of the menu based on the related target
2759  * @method _didMouseLeave
2760  * @protected
2761  * @param {HTMLElement} oRelatedTarget The related target based on which we're making the decision
2762  * @return {boolean} true if it's OK to hide based on the related target.
2763  */
2764 _didMouseLeave : function(oRelatedTarget) {
2765     // Hide if we're not moving back to the element from somewhere inside the element, or we're moving to an element inside the menu.
2766     // The shadow is treated as an edge case, inside inside the menu, but we get no further mouseouts, because it overflows the element,
2767     // so we need to close when moving to the menu. 
2768     return (oRelatedTarget === this._shadow || (oRelatedTarget != this.element && !Dom.isAncestor(this.element, oRelatedTarget)));
2769 },
2770
2771 /**
2772 * @method _onMouseMove
2773 * @description "click" event handler for the menu.
2774 * @protected
2775 * @param {Event} p_oEvent Object representing the DOM event object passed 
2776 * back by the event utility (YAHOO.util.Event).
2777 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
2778 * fired the event.
2779 */
2780 _onMouseMove: function (p_oEvent, p_oMenu) {
2781
2782     if (!this._bStopMouseEventHandlers) {
2783     
2784         this._nCurrentMouseX = Event.getPageX(p_oEvent);
2785     
2786     }
2787
2788 },
2789
2790
2791 /**
2792 * @method _onClick
2793 * @description "click" event handler for the menu.
2794 * @protected
2795 * @param {String} p_sType String representing the name of the event that 
2796 * was fired.
2797 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
2798 */
2799 _onClick: function (p_sType, p_aArgs) {
2800
2801     var oEvent = p_aArgs[0],
2802         oItem = p_aArgs[1],
2803         bInMenuAnchor = false,
2804         oSubmenu,
2805         oMenu,
2806         oRoot,
2807         sId,
2808         sURL,
2809         nHashPos,
2810         nLen;
2811
2812
2813     var hide = function () {
2814         
2815         oRoot = this.getRoot();
2816
2817         if (oRoot instanceof YAHOO.widget.MenuBar || 
2818             oRoot.cfg.getProperty(_POSITION) == _STATIC) {
2819
2820             oRoot.clearActiveItem();
2821
2822         }
2823         else {
2824
2825             oRoot.hide();
2826         
2827         }
2828     
2829     };
2830
2831
2832     if (oItem) {
2833     
2834         if (oItem.cfg.getProperty(_DISABLED)) {
2835         
2836             Event.preventDefault(oEvent);
2837
2838             hide.call(this);
2839
2840         }
2841         else {
2842
2843             oSubmenu = oItem.cfg.getProperty(_SUBMENU);
2844     
2845             
2846             /*
2847                  Check if the URL of the anchor is pointing to an element that is 
2848                  a child of the menu.
2849             */
2850             
2851             sURL = oItem.cfg.getProperty(_URL);
2852
2853         
2854             if (sURL) {
2855     
2856                 nHashPos = sURL.indexOf(_HASH);
2857     
2858                 nLen = sURL.length;
2859     
2860     
2861                 if (nHashPos != -1) {
2862     
2863                     sURL = sURL.substr(nHashPos, nLen);
2864         
2865                     nLen = sURL.length;
2866     
2867     
2868                     if (nLen > 1) {
2869     
2870                         sId = sURL.substr(1, nLen);
2871     
2872                         oMenu = YAHOO.widget.MenuManager.getMenu(sId);
2873                         
2874                         if (oMenu) {
2875
2876                             bInMenuAnchor = 
2877                                 (this.getRoot() === oMenu.getRoot());
2878
2879                         }
2880                         
2881                     }
2882                     else if (nLen === 1) {
2883     
2884                         bInMenuAnchor = true;
2885                     
2886                     }
2887     
2888                 }
2889             
2890             }
2891
2892     
2893             if (bInMenuAnchor && !oItem.cfg.getProperty(_TARGET)) {
2894     
2895                 Event.preventDefault(oEvent);
2896                 
2897
2898                 if (UA.webkit) {
2899                 
2900                     oItem.focus();
2901                 
2902                 }
2903                 else {
2904
2905                     oItem.focusEvent.fire();
2906                 
2907                 }
2908             
2909             }
2910     
2911     
2912             if (!oSubmenu && !this.cfg.getProperty(_KEEP_OPEN)) {
2913     
2914                 hide.call(this);
2915     
2916             }
2917             
2918         }
2919     
2920     }
2921
2922 },
2923
2924 /*
2925     This function is called to prevent a bug in Firefox.  In Firefox,
2926     moving a DOM element into a stationary mouse pointer will cause the 
2927     browser to fire mouse events.  This can result in the menu mouse
2928     event handlers being called uncessarily, especially when menus are 
2929     moved into a stationary mouse pointer as a result of a 
2930     key event handler.
2931 */
2932 /**
2933  * Utility method to stop mouseevents from being fired if the DOM
2934  * changes under a stationary mouse pointer (as opposed to the mouse moving
2935  * over a DOM element).
2936  * 
2937  * @method _stopMouseEventHandlers
2938  * @private
2939  */
2940 _stopMouseEventHandlers: function() {
2941     this._bStopMouseEventHandlers = true;
2942
2943     Lang.later(10, this, function () {
2944         this._bStopMouseEventHandlers = false;
2945     });
2946 },
2947
2948 /**
2949 * @method _onKeyDown
2950 * @description "keydown" event handler for the menu.
2951 * @protected
2952 * @param {String} p_sType String representing the name of the event that 
2953 * was fired.
2954 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
2955 */
2956 _onKeyDown: function (p_sType, p_aArgs) {
2957
2958     var oEvent = p_aArgs[0],
2959         oItem = p_aArgs[1],
2960         oSubmenu,
2961         oItemCfg,
2962         oParentItem,
2963         oRoot,
2964         oNextItem,
2965         oBody,
2966         nBodyScrollTop,
2967         nBodyOffsetHeight,
2968         aItems,
2969         nItems,
2970         nNextItemOffsetTop,
2971         nScrollTarget,
2972         oParentMenu,
2973         oFocusedEl;
2974
2975
2976     if (this._useHideDelay) {
2977         this._cancelHideDelay();
2978     }
2979
2980     if (oItem && !oItem.cfg.getProperty(_DISABLED)) {
2981
2982         oItemCfg = oItem.cfg;
2983         oParentItem = this.parent;
2984
2985         switch(oEvent.keyCode) {
2986     
2987             case 38:    // Up arrow
2988             case 40:    // Down arrow
2989     
2990                 oNextItem = (oEvent.keyCode == 38) ? 
2991                     oItem.getPreviousEnabledSibling() : 
2992                     oItem.getNextEnabledSibling();
2993         
2994                 if (oNextItem) {
2995
2996                     this.clearActiveItem();
2997
2998                     oNextItem.cfg.setProperty(_SELECTED, true);
2999                     oNextItem.focus();
3000
3001                     if (this.cfg.getProperty(_MAX_HEIGHT) > 0 || Dom.hasClass(this.body, _YUI_MENU_BODY_SCROLLED)) {
3002
3003                         oBody = this.body;
3004                         nBodyScrollTop = oBody.scrollTop;
3005                         nBodyOffsetHeight = oBody.offsetHeight;
3006                         aItems = this.getItems();
3007                         nItems = aItems.length - 1;
3008                         nNextItemOffsetTop = oNextItem.element.offsetTop;
3009
3010
3011                         if (oEvent.keyCode == 40 ) {    // Down
3012                        
3013                             if (nNextItemOffsetTop >= (nBodyOffsetHeight + nBodyScrollTop)) {
3014
3015                                 oBody.scrollTop = nNextItemOffsetTop - nBodyOffsetHeight;
3016
3017                             }
3018                             else if (nNextItemOffsetTop <= nBodyScrollTop) {
3019                             
3020                                 oBody.scrollTop = 0;
3021                             
3022                             }
3023
3024
3025                             if (oNextItem == aItems[nItems]) {
3026
3027                                 oBody.scrollTop = oNextItem.element.offsetTop;
3028
3029                             }
3030
3031                         }
3032                         else {  // Up
3033
3034                             if (nNextItemOffsetTop <= nBodyScrollTop) {
3035
3036                                 oBody.scrollTop = nNextItemOffsetTop - oNextItem.element.offsetHeight;
3037                             
3038                             }
3039                             else if (nNextItemOffsetTop >= (nBodyScrollTop + nBodyOffsetHeight)) {
3040                             
3041                                 oBody.scrollTop = nNextItemOffsetTop;
3042                             
3043                             }
3044
3045
3046                             if (oNextItem == aItems[0]) {
3047                             
3048                                 oBody.scrollTop = 0;
3049                             
3050                             }
3051
3052                         }
3053
3054
3055                         nBodyScrollTop = oBody.scrollTop;
3056                         nScrollTarget = oBody.scrollHeight - oBody.offsetHeight;
3057
3058                         if (nBodyScrollTop === 0) {
3059
3060                             this._disableScrollHeader();
3061                             this._enableScrollFooter();
3062
3063                         }
3064                         else if (nBodyScrollTop == nScrollTarget) {
3065
3066                              this._enableScrollHeader();
3067                              this._disableScrollFooter();
3068
3069                         }
3070                         else {
3071
3072                             this._enableScrollHeader();
3073                             this._enableScrollFooter();
3074
3075                         }
3076
3077                     }
3078
3079                 }
3080
3081     
3082                 Event.preventDefault(oEvent);
3083
3084                 this._stopMouseEventHandlers();
3085     
3086             break;
3087             
3088     
3089             case 39:    // Right arrow
3090     
3091                 oSubmenu = oItemCfg.getProperty(_SUBMENU);
3092     
3093                 if (oSubmenu) {
3094     
3095                     if (!oItemCfg.getProperty(_SELECTED)) {
3096         
3097                         oItemCfg.setProperty(_SELECTED, true);
3098         
3099                     }
3100     
3101                     oSubmenu.show();
3102                     oSubmenu.setInitialFocus();
3103                     oSubmenu.setInitialSelection();
3104     
3105                 }
3106                 else {
3107     
3108                     oRoot = this.getRoot();
3109                     
3110                     if (oRoot instanceof YAHOO.widget.MenuBar) {
3111     
3112                         oNextItem = oRoot.activeItem.getNextEnabledSibling();
3113     
3114                         if (oNextItem) {
3115                         
3116                             oRoot.clearActiveItem();
3117     
3118                             oNextItem.cfg.setProperty(_SELECTED, true);
3119     
3120                             oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
3121     
3122                             if (oSubmenu) {
3123     
3124                                 oSubmenu.show();
3125                                 oSubmenu.setInitialFocus();
3126                             
3127                             }
3128                             else {
3129     
3130                                 oNextItem.focus();
3131                             
3132                             }
3133                         
3134                         }
3135                     
3136                     }
3137                 
3138                 }
3139     
3140     
3141                 Event.preventDefault(oEvent);
3142
3143                 this._stopMouseEventHandlers();
3144
3145             break;
3146     
3147     
3148             case 37:    // Left arrow
3149     
3150                 if (oParentItem) {
3151     
3152                     oParentMenu = oParentItem.parent;
3153     
3154                     if (oParentMenu instanceof YAHOO.widget.MenuBar) {
3155     
3156                         oNextItem = 
3157                             oParentMenu.activeItem.getPreviousEnabledSibling();
3158     
3159                         if (oNextItem) {
3160                         
3161                             oParentMenu.clearActiveItem();
3162     
3163                             oNextItem.cfg.setProperty(_SELECTED, true);
3164     
3165                             oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
3166     
3167                             if (oSubmenu) {
3168                             
3169                                 oSubmenu.show();
3170                                 oSubmenu.setInitialFocus();
3171                             
3172                             }
3173                             else {
3174     
3175                                 oNextItem.focus();
3176                             
3177                             }
3178                         
3179                         } 
3180                     
3181                     }
3182                     else {
3183     
3184                         this.hide();
3185     
3186                         oParentItem.focus();
3187                     
3188                     }
3189     
3190                 }
3191     
3192                 Event.preventDefault(oEvent);
3193
3194                 this._stopMouseEventHandlers();
3195
3196             break;        
3197     
3198         }
3199
3200
3201     }
3202
3203
3204     if (oEvent.keyCode == 27) { // Esc key
3205
3206         if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {
3207         
3208             this.hide();
3209
3210             if (this.parent) {
3211
3212                 this.parent.focus();
3213             
3214             }
3215             else {
3216                 // Focus the element that previously had focus
3217
3218                 oFocusedEl = this._focusedElement;
3219
3220                 if (oFocusedEl && oFocusedEl.focus) {
3221
3222                     try {
3223                         oFocusedEl.focus();
3224                     }
3225                     catch(ex) {
3226                     }
3227
3228                 }
3229                 
3230             }
3231
3232         }
3233         else if (this.activeItem) {
3234
3235             oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU);
3236
3237             if (oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) {
3238             
3239                 oSubmenu.hide();
3240                 this.activeItem.focus();
3241             
3242             }
3243             else {
3244
3245                 this.activeItem.blur();
3246                 this.activeItem.cfg.setProperty(_SELECTED, false);
3247         
3248             }
3249         
3250         }
3251
3252
3253         Event.preventDefault(oEvent);
3254     
3255     }
3256     
3257 },
3258
3259
3260 /**
3261 * @method _onKeyPress
3262 * @description "keypress" event handler for a Menu instance.
3263 * @protected
3264 * @param {String} p_sType The name of the event that was fired.
3265 * @param {Array} p_aArgs Collection of arguments sent when the event 
3266 * was fired.
3267 */
3268 _onKeyPress: function (p_sType, p_aArgs) {
3269     
3270     var oEvent = p_aArgs[0];
3271
3272
3273     if (oEvent.keyCode == 40 || oEvent.keyCode == 38) {
3274
3275         Event.preventDefault(oEvent);
3276
3277     }
3278
3279 },
3280
3281
3282 /**
3283 * @method _onBlur
3284 * @description "blur" event handler for a Menu instance.
3285 * @protected
3286 * @param {String} p_sType The name of the event that was fired.
3287 * @param {Array} p_aArgs Collection of arguments sent when the event 
3288 * was fired.
3289 */
3290 _onBlur: function (p_sType, p_aArgs) {
3291         
3292     if (this._hasFocus) {
3293         this._hasFocus = false;
3294     }
3295
3296 },
3297
3298 /**
3299 * @method _onYChange
3300 * @description "y" event handler for a Menu instance.
3301 * @protected
3302 * @param {String} p_sType The name of the event that was fired.
3303 * @param {Array} p_aArgs Collection of arguments sent when the event 
3304 * was fired.
3305 */
3306 _onYChange: function (p_sType, p_aArgs) {
3307
3308     var oParent = this.parent,
3309         nScrollTop,
3310         oIFrame,
3311         nY;
3312
3313
3314     if (oParent) {
3315
3316         nScrollTop = oParent.parent.body.scrollTop;
3317
3318
3319         if (nScrollTop > 0) {
3320     
3321             nY = (this.cfg.getProperty(_Y) - nScrollTop);
3322             
3323             Dom.setY(this.element, nY);
3324
3325             oIFrame = this.iframe;            
3326     
3327
3328             if (oIFrame) {
3329     
3330                 Dom.setY(oIFrame, nY);
3331     
3332             }
3333             
3334             this.cfg.setProperty(_Y, nY, true);
3335         
3336         }
3337     
3338     }
3339
3340 },
3341
3342
3343 /**
3344 * @method _onScrollTargetMouseOver
3345 * @description "mouseover" event handler for the menu's "header" and "footer" 
3346 * elements.  Used to scroll the body of the menu up and down when the 
3347 * menu's "maxheight" configuration property is set to a value greater than 0.
3348 * @protected
3349 * @param {Event} p_oEvent Object representing the DOM event object passed 
3350 * back by the event utility (YAHOO.util.Event).
3351 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
3352 * fired the event.
3353 */
3354 _onScrollTargetMouseOver: function (p_oEvent, p_oMenu) {
3355
3356     var oBodyScrollTimer = this._bodyScrollTimer;
3357
3358
3359     if (oBodyScrollTimer) {
3360
3361         oBodyScrollTimer.cancel();
3362
3363     }
3364
3365
3366     this._cancelHideDelay();
3367
3368
3369     var oTarget = Event.getTarget(p_oEvent),
3370         oBody = this.body,
3371         nScrollIncrement = this.cfg.getProperty(_SCROLL_INCREMENT),
3372         nScrollTarget,
3373         fnScrollFunction;
3374
3375
3376     function scrollBodyDown() {
3377
3378         var nScrollTop = oBody.scrollTop;
3379
3380
3381         if (nScrollTop < nScrollTarget) {
3382
3383             oBody.scrollTop = (nScrollTop + nScrollIncrement);
3384
3385             this._enableScrollHeader();
3386
3387         }
3388         else {
3389
3390             oBody.scrollTop = nScrollTarget;
3391
3392             this._bodyScrollTimer.cancel();
3393
3394             this._disableScrollFooter();
3395
3396         }
3397
3398     }
3399
3400
3401     function scrollBodyUp() {
3402
3403         var nScrollTop = oBody.scrollTop;
3404
3405
3406         if (nScrollTop > 0) {
3407
3408             oBody.scrollTop = (nScrollTop - nScrollIncrement);
3409
3410             this._enableScrollFooter();
3411
3412         }
3413         else {
3414
3415             oBody.scrollTop = 0;
3416
3417             this._bodyScrollTimer.cancel();
3418
3419             this._disableScrollHeader();
3420
3421         }
3422
3423     }
3424
3425     
3426     if (Dom.hasClass(oTarget, _HD)) {
3427
3428         fnScrollFunction = scrollBodyUp;
3429     
3430     }
3431     else {
3432
3433         nScrollTarget = oBody.scrollHeight - oBody.offsetHeight;
3434
3435         fnScrollFunction = scrollBodyDown;
3436     
3437     }
3438     
3439
3440     this._bodyScrollTimer = Lang.later(10, this, fnScrollFunction, null, true);
3441
3442 },
3443
3444
3445 /**
3446 * @method _onScrollTargetMouseOut
3447 * @description "mouseout" event handler for the menu's "header" and "footer" 
3448 * elements.  Used to stop scrolling the body of the menu up and down when the 
3449 * menu's "maxheight" configuration property is set to a value greater than 0.
3450 * @protected
3451 * @param {Event} p_oEvent Object representing the DOM event object passed 
3452 * back by the event utility (YAHOO.util.Event).
3453 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
3454 * fired the event.
3455 */
3456 _onScrollTargetMouseOut: function (p_oEvent, p_oMenu) {
3457
3458     var oBodyScrollTimer = this._bodyScrollTimer;
3459
3460     if (oBodyScrollTimer) {
3461
3462         oBodyScrollTimer.cancel();
3463
3464     }
3465     
3466     this._cancelHideDelay();
3467
3468 },
3469
3470
3471
3472 // Private methods
3473
3474
3475 /**
3476 * @method _onInit
3477 * @description "init" event handler for the menu.
3478 * @private
3479 * @param {String} p_sType String representing the name of the event that 
3480 * was fired.
3481 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
3482 */
3483 _onInit: function (p_sType, p_aArgs) {
3484
3485     this.cfg.subscribeToConfigEvent(_VISIBLE, this._onVisibleChange);
3486
3487     var bRootMenu = !this.parent,
3488         bLazyLoad = this.lazyLoad;
3489
3490
3491     /*
3492         Automatically initialize a menu's subtree if:
3493
3494         1) This is the root menu and lazyload is off
3495         
3496         2) This is the root menu, lazyload is on, but the menu is 
3497            already visible
3498
3499         3) This menu is a submenu and lazyload is off
3500     */
3501
3502
3503
3504     if (((bRootMenu && !bLazyLoad) || 
3505         (bRootMenu && (this.cfg.getProperty(_VISIBLE) || 
3506         this.cfg.getProperty(_POSITION) == _STATIC)) || 
3507         (!bRootMenu && !bLazyLoad)) && this.getItemGroups().length === 0) {
3508
3509         if (this.srcElement) {
3510
3511             this._initSubTree();
3512         
3513         }
3514
3515
3516         if (this.itemData) {
3517
3518             this.addItems(this.itemData);
3519
3520         }
3521     
3522     }
3523     else if (bLazyLoad) {
3524
3525         this.cfg.fireQueue();
3526     
3527     }
3528
3529 },
3530
3531
3532 /**
3533 * @method _onBeforeRender
3534 * @description "beforerender" event handler for the menu.  Appends all of the 
3535 * <code>&#60;ul&#62;</code>, <code>&#60;li&#62;</code> and their accompanying 
3536 * title elements to the body element of the menu.
3537 * @private
3538 * @param {String} p_sType String representing the name of the event that 
3539 * was fired.
3540 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
3541 */
3542 _onBeforeRender: function (p_sType, p_aArgs) {
3543
3544     var oEl = this.element,
3545         nListElements = this._aListElements.length,
3546         bFirstList = true,
3547         i = 0,
3548         oUL,
3549         oGroupTitle;
3550
3551     if (nListElements > 0) {
3552
3553         do {
3554
3555             oUL = this._aListElements[i];
3556
3557             if (oUL) {
3558
3559                 if (bFirstList) {
3560         
3561                     Dom.addClass(oUL, _FIRST_OF_TYPE);
3562                     bFirstList = false;
3563         
3564                 }
3565
3566
3567                 if (!Dom.isAncestor(oEl, oUL)) {
3568
3569                     this.appendToBody(oUL);
3570
3571                 }
3572
3573
3574                 oGroupTitle = this._aGroupTitleElements[i];
3575
3576                 if (oGroupTitle) {
3577
3578                     if (!Dom.isAncestor(oEl, oGroupTitle)) {
3579
3580                         oUL.parentNode.insertBefore(oGroupTitle, oUL);
3581
3582                     }
3583
3584
3585                     Dom.addClass(oUL, _HAS_TITLE);
3586
3587                 }
3588
3589             }
3590
3591             i++;
3592
3593         }
3594         while (i < nListElements);
3595
3596     }
3597
3598 },
3599
3600
3601 /**
3602 * @method _onRender
3603 * @description "render" event handler for the menu.
3604 * @private
3605 * @param {String} p_sType String representing the name of the event that 
3606 * was fired.
3607 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
3608 */
3609 _onRender: function (p_sType, p_aArgs) {
3610
3611     if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { 
3612
3613         if (!this.cfg.getProperty(_VISIBLE)) {
3614
3615             this.positionOffScreen();
3616
3617         }
3618     
3619     }
3620
3621 },
3622
3623
3624
3625
3626
3627 /**
3628 * @method _onBeforeShow
3629 * @description "beforeshow" event handler for the menu.
3630 * @private
3631 * @param {String} p_sType String representing the name of the event that 
3632 * was fired.
3633 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
3634 */
3635 _onBeforeShow: function (p_sType, p_aArgs) {
3636
3637     var nOptions,
3638         n,
3639         oSrcElement,
3640         oContainer = this.cfg.getProperty(_CONTAINER);
3641
3642
3643     if (this.lazyLoad && this.getItemGroups().length === 0) {
3644
3645         if (this.srcElement) {
3646         
3647             this._initSubTree();
3648
3649         }
3650
3651
3652         if (this.itemData) {
3653
3654             if (this.parent && this.parent.parent && 
3655                 this.parent.parent.srcElement && 
3656                 this.parent.parent.srcElement.tagName.toUpperCase() == 
3657                 _SELECT) {
3658
3659                 nOptions = this.itemData.length;
3660     
3661                 for(n=0; n<nOptions; n++) {
3662
3663                     if (this.itemData[n].tagName) {
3664
3665                         this.addItem((new this.ITEM_TYPE(this.itemData[n])));
3666     
3667                     }
3668     
3669                 }
3670             
3671             }
3672             else {
3673
3674                 this.addItems(this.itemData);
3675             
3676             }
3677         
3678         }
3679
3680
3681         oSrcElement = this.srcElement;
3682
3683         if (oSrcElement) {
3684
3685             if (oSrcElement.tagName.toUpperCase() == _SELECT) {
3686
3687                 if (Dom.inDocument(oSrcElement)) {
3688
3689                     this.render(oSrcElement.parentNode);
3690                 
3691                 }
3692                 else {
3693                 
3694                     this.render(oContainer);
3695                 
3696                 }
3697
3698             }
3699             else {
3700
3701                 this.render();
3702
3703             }
3704
3705         }
3706         else {
3707
3708             if (this.parent) {
3709
3710                 this.render(this.parent.element);     
3711
3712             }
3713             else {
3714
3715                 this.render(oContainer);
3716
3717             }                
3718
3719         }
3720
3721     }
3722
3723
3724
3725     var oParent = this.parent,
3726         aAlignment;
3727
3728
3729     if (!oParent && this.cfg.getProperty(_POSITION) == _DYNAMIC) {
3730
3731         this.cfg.refireEvent(_XY);
3732    
3733     }
3734
3735
3736     if (oParent) {
3737
3738         aAlignment = oParent.parent.cfg.getProperty(_SUBMENU_ALIGNMENT);
3739         
3740         this.cfg.setProperty(_CONTEXT, [oParent.element, aAlignment[0], aAlignment[1]]);
3741         this.align();
3742     
3743     }
3744
3745 },
3746
3747
3748 getConstrainedY: function (y) {
3749
3750     var oMenu = this,
3751     
3752         aContext = oMenu.cfg.getProperty(_CONTEXT),
3753         nInitialMaxHeight = oMenu.cfg.getProperty(_MAX_HEIGHT),
3754
3755         nMaxHeight,
3756
3757         oOverlapPositions = {
3758
3759             "trbr": true,
3760             "tlbl": true,
3761             "bltl": true,
3762             "brtr": true
3763
3764         },
3765
3766         bPotentialContextOverlap = (aContext && oOverlapPositions[aContext[1] + aContext[2]]),
3767     
3768         oMenuEl = oMenu.element,
3769         nMenuOffsetHeight = oMenuEl.offsetHeight,
3770     
3771         nViewportOffset = Overlay.VIEWPORT_OFFSET,
3772         viewPortHeight = Dom.getViewportHeight(),
3773         scrollY = Dom.getDocumentScrollTop(),
3774
3775         bCanConstrain = 
3776             (oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) + nViewportOffset < viewPortHeight),
3777
3778         nAvailableHeight,
3779
3780         oContextEl,
3781         nContextElY,
3782         nContextElHeight,
3783
3784         bFlipped = false,
3785
3786         nTopRegionHeight,
3787         nBottomRegionHeight,
3788
3789         topConstraint = scrollY + nViewportOffset,
3790         bottomConstraint = scrollY + viewPortHeight - nMenuOffsetHeight - nViewportOffset,
3791
3792         yNew = y;
3793         
3794
3795     var flipVertical = function () {
3796
3797         var nNewY;
3798     
3799         // The Menu is below the context element, flip it above
3800         if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 
3801             nNewY = (nContextElY - nMenuOffsetHeight);
3802         }
3803         else {  // The Menu is above the context element, flip it below
3804             nNewY = (nContextElY + nContextElHeight);
3805         }
3806
3807         oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true);
3808         
3809         return nNewY;
3810     
3811     };
3812
3813
3814     /*
3815          Uses the context element's position to calculate the availble height 
3816          above and below it to display its corresponding Menu.
3817     */
3818
3819     var getDisplayRegionHeight = function () {
3820         //Bug43168: since the initial value Y is empty, the case is calculated in wrong way
3821         var _y = oMenu.cfg.getProperty(_Y);
3822         // The Menu is below the context element
3823         if ( _y == 0 || (_y - scrollY) > nContextElY ) {
3824             return (nBottomRegionHeight - nViewportOffset);                             
3825         }
3826         else {  // The Menu is above the context element
3827             return (nTopRegionHeight - nViewportOffset);                                
3828         }
3829
3830     };
3831
3832
3833     /*
3834         Sets the Menu's "y" configuration property to the correct value based on its
3835         current orientation.
3836     */ 
3837
3838     var alignY = function () {
3839
3840         var nNewY;
3841
3842         if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 
3843             nNewY = (nContextElY + nContextElHeight);
3844         }
3845         else {  
3846             nNewY = (nContextElY - oMenuEl.offsetHeight);
3847         }
3848
3849         oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true);
3850     
3851     };
3852
3853
3854     //  Resets the maxheight of the Menu to the value set by the user
3855
3856     var resetMaxHeight = function () {
3857
3858         oMenu._setScrollHeight(this.cfg.getProperty(_MAX_HEIGHT));
3859
3860         oMenu.hideEvent.unsubscribe(resetMaxHeight);
3861     
3862     };
3863
3864
3865     /*
3866         Trys to place the Menu in the best possible position (either above or 
3867         below its corresponding context element).
3868     */
3869
3870     var setVerticalPosition = function () {
3871
3872         var nDisplayRegionHeight = getDisplayRegionHeight(),
3873             bMenuHasItems = (oMenu.getItems().length > 0),
3874             nMenuMinScrollHeight,
3875             fnReturnVal;
3876
3877
3878         if (nMenuOffsetHeight > nDisplayRegionHeight) {
3879
3880             nMenuMinScrollHeight = 
3881                 bMenuHasItems ? oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) : nMenuOffsetHeight;
3882
3883
3884             if ((nDisplayRegionHeight > nMenuMinScrollHeight) && bMenuHasItems) {
3885                 nMaxHeight = nDisplayRegionHeight;
3886             }
3887             else {
3888                 nMaxHeight = nInitialMaxHeight;
3889             }
3890
3891
3892             oMenu._setScrollHeight(nMaxHeight);
3893             oMenu.hideEvent.subscribe(resetMaxHeight);
3894             
3895
3896             // Re-align the Menu since its height has just changed
3897             // as a result of the setting of the maxheight property.
3898
3899             alignY();
3900             
3901
3902             if (nDisplayRegionHeight < nMenuMinScrollHeight) {
3903
3904                 if (bFlipped) {
3905     
3906                     /*
3907                          All possible positions and values for the "maxheight" 
3908                          configuration property have been tried, but none were 
3909                          successful, so fall back to the original size and position.
3910                     */
3911
3912                     flipVertical();
3913                     
3914                 }
3915                 else {
3916     
3917                     flipVertical();
3918
3919                     bFlipped = true;
3920     
3921                     fnReturnVal = setVerticalPosition();
3922     
3923                 }
3924                 
3925             }
3926         
3927         }
3928         else if (nMaxHeight && (nMaxHeight !== nInitialMaxHeight)) {
3929         
3930             oMenu._setScrollHeight(nInitialMaxHeight);
3931             oMenu.hideEvent.subscribe(resetMaxHeight);
3932
3933             // Re-align the Menu since its height has just changed
3934             // as a result of the setting of the maxheight property.
3935
3936             alignY();
3937         
3938         }
3939
3940         return fnReturnVal;
3941
3942     };
3943
3944
3945     // Determine if the current value for the Menu's "y" configuration property will
3946     // result in the Menu being positioned outside the boundaries of the viewport
3947
3948     if (y < topConstraint || y  > bottomConstraint) {
3949
3950         // The current value for the Menu's "y" configuration property WILL
3951         // result in the Menu being positioned outside the boundaries of the viewport
3952
3953         if (bCanConstrain) {
3954
3955             if (oMenu.cfg.getProperty(_PREVENT_CONTEXT_OVERLAP) && bPotentialContextOverlap) {
3956         
3957                 //      SOLUTION #1:
3958                 //      If the "preventcontextoverlap" configuration property is set to "true", 
3959                 //      try to flip and/or scroll the Menu to both keep it inside the boundaries of the 
3960                 //      viewport AND from overlaping its context element (MenuItem or MenuBarItem).
3961
3962                 oContextEl = aContext[0];
3963                 nContextElHeight = oContextEl.offsetHeight;
3964                 nContextElY = (Dom.getY(oContextEl) - scrollY);
3965     
3966                 nTopRegionHeight = nContextElY;
3967                 nBottomRegionHeight = (viewPortHeight - (nContextElY + nContextElHeight));
3968     
3969                 setVerticalPosition();
3970                 
3971                 yNew = oMenu.cfg.getProperty(_Y);
3972         
3973             }
3974             else if (!(oMenu instanceof YAHOO.widget.MenuBar) && 
3975                 nMenuOffsetHeight >= viewPortHeight) {
3976
3977                 //      SOLUTION #2:
3978                 //      If the Menu exceeds the height of the viewport, introduce scroll bars
3979                 //      to keep the Menu inside the boundaries of the viewport
3980
3981                 nAvailableHeight = (viewPortHeight - (nViewportOffset * 2));
3982         
3983                 if (nAvailableHeight > oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT)) {
3984         
3985                     oMenu._setScrollHeight(nAvailableHeight);
3986                     oMenu.hideEvent.subscribe(resetMaxHeight);
3987         
3988                     alignY();
3989                     
3990                     yNew = oMenu.cfg.getProperty(_Y);
3991                 
3992                 }
3993         
3994             }   
3995             else {
3996
3997                 //      SOLUTION #3:
3998             
3999                 if (y < topConstraint) {
4000                     yNew  = topConstraint;
4001                 } else if (y  > bottomConstraint) {
4002                     yNew  = bottomConstraint;
4003                 }                               
4004             
4005             }
4006
4007         }
4008         else {
4009             //  The "y" configuration property cannot be set to a value that will keep
4010             //  entire Menu inside the boundary of the viewport.  Therefore, set  
4011             //  the "y" configuration property to scrollY to keep as much of the 
4012             //  Menu inside the viewport as possible.
4013             yNew = nViewportOffset + scrollY;
4014         }       
4015
4016     }
4017
4018     return yNew;
4019
4020 },
4021
4022
4023 /**
4024 * @method _onHide
4025 * @description "hide" event handler for the menu.
4026 * @private
4027 * @param {String} p_sType String representing the name of the event that 
4028 * was fired.
4029 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4030 */
4031 _onHide: function (p_sType, p_aArgs) {
4032
4033     if (this.cfg.getProperty(_POSITION) === _DYNAMIC) {
4034     
4035         this.positionOffScreen();
4036     
4037     }
4038
4039 },
4040
4041
4042 /**
4043 * @method _onShow
4044 * @description "show" event handler for the menu.
4045 * @private
4046 * @param {String} p_sType String representing the name of the event that 
4047 * was fired.
4048 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4049 */
4050 _onShow: function (p_sType, p_aArgs) {
4051
4052     var oParent = this.parent,
4053         oParentMenu,
4054         oElement,
4055         nOffsetWidth,
4056         sWidth;        
4057
4058
4059     function disableAutoSubmenuDisplay(p_oEvent) {
4060
4061         var oTarget;
4062
4063         if (p_oEvent.type == _MOUSEDOWN || (p_oEvent.type == _KEYDOWN && p_oEvent.keyCode == 27)) {
4064
4065             /*  
4066                 Set the "autosubmenudisplay" to "false" if the user
4067                 clicks outside the menu bar.
4068             */
4069
4070             oTarget = Event.getTarget(p_oEvent);
4071
4072             if (oTarget != oParentMenu.element || !Dom.isAncestor(oParentMenu.element, oTarget)) {
4073
4074                 oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false);
4075
4076                 Event.removeListener(document, _MOUSEDOWN, disableAutoSubmenuDisplay);
4077                 Event.removeListener(document, _KEYDOWN, disableAutoSubmenuDisplay);
4078
4079             }
4080         
4081         }
4082
4083     }
4084
4085
4086     function onSubmenuHide(p_sType, p_aArgs, p_sWidth) {
4087     
4088         this.cfg.setProperty(_WIDTH, _EMPTY_STRING);
4089         this.hideEvent.unsubscribe(onSubmenuHide, p_sWidth);
4090     
4091     }
4092
4093
4094     if (oParent) {
4095
4096         oParentMenu = oParent.parent;
4097
4098
4099         if (!oParentMenu.cfg.getProperty(_AUTO_SUBMENU_DISPLAY) && 
4100             (oParentMenu instanceof YAHOO.widget.MenuBar || 
4101             oParentMenu.cfg.getProperty(_POSITION) == _STATIC)) {
4102
4103             oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, true);
4104
4105             Event.on(document, _MOUSEDOWN, disableAutoSubmenuDisplay);                             
4106             Event.on(document, _KEYDOWN, disableAutoSubmenuDisplay);
4107
4108         }
4109
4110
4111         //      The following fixes an issue with the selected state of a MenuItem 
4112         //      not rendering correctly when a submenu is aligned to the left of
4113         //      its parent Menu instance.
4114
4115         if ((this.cfg.getProperty("x") < oParentMenu.cfg.getProperty("x")) && 
4116             (UA.gecko && UA.gecko < 1.9) && !this.cfg.getProperty(_WIDTH)) {
4117
4118             oElement = this.element;
4119             nOffsetWidth = oElement.offsetWidth;
4120             
4121             /*
4122                 Measuring the difference of the offsetWidth before and after
4123                 setting the "width" style attribute allows us to compute the 
4124                 about of padding and borders applied to the element, which in 
4125                 turn allows us to set the "width" property correctly.
4126             */
4127             
4128             oElement.style.width = nOffsetWidth + _PX;
4129             
4130             sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX;
4131             
4132             this.cfg.setProperty(_WIDTH, sWidth);
4133         
4134             this.hideEvent.subscribe(onSubmenuHide, sWidth);
4135         
4136         }
4137
4138     }
4139
4140
4141     /*
4142         Dynamically positioned, root Menus focus themselves when visible, and 
4143         will then, when hidden, restore focus to the UI control that had focus 
4144         before the Menu was made visible.
4145     */ 
4146
4147     if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) {
4148
4149         this._focusedElement = oFocusedElement;
4150         
4151         this.focus();
4152     
4153     }
4154
4155
4156 },
4157
4158
4159 /**
4160 * @method _onBeforeHide
4161 * @description "beforehide" event handler for the menu.
4162 * @private
4163 * @param {String} p_sType String representing the name of the event that 
4164 * was fired.
4165 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4166 */
4167 _onBeforeHide: function (p_sType, p_aArgs) {
4168
4169     var oActiveItem = this.activeItem,
4170         oRoot = this.getRoot(),
4171         oConfig,
4172         oSubmenu;
4173
4174
4175     if (oActiveItem) {
4176
4177         oConfig = oActiveItem.cfg;
4178
4179         oConfig.setProperty(_SELECTED, false);
4180
4181         oSubmenu = oConfig.getProperty(_SUBMENU);
4182
4183         if (oSubmenu) {
4184
4185             oSubmenu.hide();
4186
4187         }
4188
4189     }
4190
4191
4192     /*
4193         Focus can get lost in IE when the mouse is moving from a submenu back to its parent Menu.  
4194         For this reason, it is necessary to maintain the focused state in a private property 
4195         so that the _onMouseOver event handler is able to determined whether or not to set focus
4196         to MenuItems as the user is moving the mouse.
4197     */ 
4198
4199     if (UA.ie && this.cfg.getProperty(_POSITION) === _DYNAMIC && this.parent) {
4200
4201         oRoot._hasFocus = this.hasFocus();
4202     
4203     }
4204
4205
4206     if (oRoot == this) {
4207
4208         oRoot.blur();
4209     
4210     }
4211
4212 },
4213
4214
4215 /**
4216 * @method _onParentMenuConfigChange
4217 * @description "configchange" event handler for a submenu.
4218 * @private
4219 * @param {String} p_sType String representing the name of the event that 
4220 * was fired.
4221 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4222 * @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 
4223 * subscribed to the event.
4224 */
4225 _onParentMenuConfigChange: function (p_sType, p_aArgs, p_oSubmenu) {
4226     
4227     var sPropertyName = p_aArgs[0][0],
4228         oPropertyValue = p_aArgs[0][1];
4229
4230     switch(sPropertyName) {
4231
4232         case _IFRAME:
4233         case _CONSTRAIN_TO_VIEWPORT:
4234         case _HIDE_DELAY:
4235         case _SHOW_DELAY:
4236         case _SUBMENU_HIDE_DELAY:
4237         case _CLICK_TO_HIDE:
4238         case _EFFECT:
4239         case _CLASSNAME:
4240         case _SCROLL_INCREMENT:
4241         case _MAX_HEIGHT:
4242         case _MIN_SCROLL_HEIGHT:
4243         case _MONITOR_RESIZE:
4244         case _SHADOW:
4245         case _PREVENT_CONTEXT_OVERLAP:
4246         case _KEEP_OPEN:
4247
4248             p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
4249                 
4250         break;
4251         
4252         case _SUBMENU_ALIGNMENT:
4253
4254             if (!(this.parent.parent instanceof YAHOO.widget.MenuBar)) {
4255         
4256                 p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
4257         
4258             }
4259         
4260         break;
4261         
4262     }
4263     
4264 },
4265
4266
4267 /**
4268 * @method _onParentMenuRender
4269 * @description "render" event handler for a submenu.  Renders a  
4270 * submenu in response to the firing of its parent's "render" event.
4271 * @private
4272 * @param {String} p_sType String representing the name of the event that 
4273 * was fired.
4274 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4275 * @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 
4276 * subscribed to the event.
4277 */
4278 _onParentMenuRender: function (p_sType, p_aArgs, p_oSubmenu) {
4279
4280     var oParentMenu = p_oSubmenu.parent.parent,
4281         oParentCfg = oParentMenu.cfg,
4282
4283         oConfig = {
4284
4285             constraintoviewport: oParentCfg.getProperty(_CONSTRAIN_TO_VIEWPORT),
4286
4287             xy: [0,0],
4288
4289             clicktohide: oParentCfg.getProperty(_CLICK_TO_HIDE),
4290                 
4291             effect: oParentCfg.getProperty(_EFFECT),
4292
4293             showdelay: oParentCfg.getProperty(_SHOW_DELAY),
4294             
4295             hidedelay: oParentCfg.getProperty(_HIDE_DELAY),
4296
4297             submenuhidedelay: oParentCfg.getProperty(_SUBMENU_HIDE_DELAY),
4298
4299             classname: oParentCfg.getProperty(_CLASSNAME),
4300             
4301             scrollincrement: oParentCfg.getProperty(_SCROLL_INCREMENT),
4302             
4303             maxheight: oParentCfg.getProperty(_MAX_HEIGHT),
4304
4305             minscrollheight: oParentCfg.getProperty(_MIN_SCROLL_HEIGHT),
4306             
4307             iframe: oParentCfg.getProperty(_IFRAME),
4308             
4309             shadow: oParentCfg.getProperty(_SHADOW),
4310
4311             preventcontextoverlap: oParentCfg.getProperty(_PREVENT_CONTEXT_OVERLAP),
4312             
4313             monitorresize: oParentCfg.getProperty(_MONITOR_RESIZE),
4314
4315             keepopen: oParentCfg.getProperty(_KEEP_OPEN)
4316
4317         },
4318         
4319         oLI;
4320
4321
4322     
4323     if (!(oParentMenu instanceof YAHOO.widget.MenuBar)) {
4324
4325         oConfig[_SUBMENU_ALIGNMENT] = oParentCfg.getProperty(_SUBMENU_ALIGNMENT);
4326
4327     }
4328
4329
4330     p_oSubmenu.cfg.applyConfig(oConfig);
4331
4332
4333     if (!this.lazyLoad) {
4334
4335         oLI = this.parent.element;
4336
4337         if (this.element.parentNode == oLI) {
4338     
4339             this.render();
4340     
4341         }
4342         else {
4343
4344             this.render(oLI);
4345     
4346         }
4347
4348     }
4349     
4350 },
4351
4352
4353 /**
4354 * @method _onMenuItemDestroy
4355 * @description "destroy" event handler for the menu's items.
4356 * @private
4357 * @param {String} p_sType String representing the name of the event 
4358 * that was fired.
4359 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4360 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
4361 * that fired the event.
4362 */
4363 _onMenuItemDestroy: function (p_sType, p_aArgs, p_oItem) {
4364
4365     this._removeItemFromGroupByValue(p_oItem.groupIndex, p_oItem);
4366
4367 },
4368
4369
4370 /**
4371 * @method _onMenuItemConfigChange
4372 * @description "configchange" event handler for the menu's items.
4373 * @private
4374 * @param {String} p_sType String representing the name of the event that 
4375 * was fired.
4376 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4377 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
4378 * that fired the event.
4379 */
4380 _onMenuItemConfigChange: function (p_sType, p_aArgs, p_oItem) {
4381
4382     var sPropertyName = p_aArgs[0][0],
4383         oPropertyValue = p_aArgs[0][1],
4384         oSubmenu;
4385
4386
4387     switch(sPropertyName) {
4388
4389         case _SELECTED:
4390
4391             if (oPropertyValue === true) {
4392
4393                 this.activeItem = p_oItem;
4394             
4395             }
4396
4397         break;
4398
4399         case _SUBMENU:
4400
4401             oSubmenu = p_aArgs[0][1];
4402
4403             if (oSubmenu) {
4404
4405                 this._configureSubmenu(p_oItem);
4406
4407             }
4408
4409         break;
4410
4411     }
4412
4413 },
4414
4415
4416
4417 // Public event handlers for configuration properties
4418
4419
4420 /**
4421 * @method configVisible
4422 * @description Event handler for when the "visible" configuration property 
4423 * the menu changes.
4424 * @param {String} p_sType String representing the name of the event that 
4425 * was fired.
4426 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4427 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4428 * fired the event.
4429 */
4430 configVisible: function (p_sType, p_aArgs, p_oMenu) {
4431
4432     var bVisible,
4433         sDisplay;
4434
4435     if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {
4436
4437         Menu.superclass.configVisible.call(this, p_sType, p_aArgs, p_oMenu);
4438
4439     }
4440     else {
4441
4442         bVisible = p_aArgs[0];
4443         sDisplay = Dom.getStyle(this.element, _DISPLAY);
4444
4445         Dom.setStyle(this.element, _VISIBILITY, _VISIBLE);
4446
4447         if (bVisible) {
4448
4449             if (sDisplay != _BLOCK) {
4450                 this.beforeShowEvent.fire();
4451                 Dom.setStyle(this.element, _DISPLAY, _BLOCK);
4452                 this.showEvent.fire();
4453             }
4454         
4455         }
4456         else {
4457
4458             if (sDisplay == _BLOCK) {
4459                 this.beforeHideEvent.fire();
4460                 Dom.setStyle(this.element, _DISPLAY, _NONE);
4461                 this.hideEvent.fire();
4462             }
4463         
4464         }
4465
4466     }
4467
4468 },
4469
4470
4471 /**
4472 * @method configPosition
4473 * @description Event handler for when the "position" configuration property 
4474 * of the menu changes.
4475 * @param {String} p_sType String representing the name of the event that 
4476 * was fired.
4477 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4478 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4479 * fired the event.
4480 */
4481 configPosition: function (p_sType, p_aArgs, p_oMenu) {
4482
4483     var oElement = this.element,
4484         sCSSPosition = p_aArgs[0] == _STATIC ? _STATIC : _ABSOLUTE,
4485         oCfg = this.cfg,
4486         nZIndex;
4487
4488
4489     Dom.setStyle(oElement, _POSITION, sCSSPosition);
4490
4491
4492     if (sCSSPosition == _STATIC) {
4493
4494         // Statically positioned menus are visible by default
4495         
4496         Dom.setStyle(oElement, _DISPLAY, _BLOCK);
4497
4498         oCfg.setProperty(_VISIBLE, true);
4499
4500     }
4501     else {
4502
4503         /*
4504             Even though the "visible" property is queued to 
4505             "false" by default, we need to set the "visibility" property to 
4506             "hidden" since Overlay's "configVisible" implementation checks the 
4507             element's "visibility" style property before deciding whether 
4508             or not to show an Overlay instance.
4509         */
4510
4511         Dom.setStyle(oElement, _VISIBILITY, _HIDDEN);
4512     
4513     }
4514
4515
4516      if (sCSSPosition == _ABSOLUTE) {
4517          nZIndex = oCfg.getProperty(_ZINDEX);
4518
4519          if (!nZIndex || nZIndex === 0) {
4520              oCfg.setProperty(_ZINDEX, 1);
4521          }
4522
4523      }
4524
4525 },
4526
4527
4528 /**
4529 * @method configIframe
4530 * @description Event handler for when the "iframe" configuration property of 
4531 * the menu changes.
4532 * @param {String} p_sType String representing the name of the event that 
4533 * was fired.
4534 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4535 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4536 * fired the event.
4537 */
4538 configIframe: function (p_sType, p_aArgs, p_oMenu) {    
4539
4540     if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {
4541
4542         Menu.superclass.configIframe.call(this, p_sType, p_aArgs, p_oMenu);
4543
4544     }
4545
4546 },
4547
4548
4549 /**
4550 * @method configHideDelay
4551 * @description Event handler for when the "hidedelay" configuration property 
4552 * of the menu changes.
4553 * @param {String} p_sType String representing the name of the event that 
4554 * was fired.
4555 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4556 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4557 * fired the event.
4558 */
4559 configHideDelay: function (p_sType, p_aArgs, p_oMenu) {
4560
4561     var nHideDelay = p_aArgs[0];
4562
4563     this._useHideDelay = (nHideDelay > 0);
4564
4565 },
4566
4567
4568 /**
4569 * @method configContainer
4570 * @description Event handler for when the "container" configuration property 
4571 * of the menu changes.
4572 * @param {String} p_sType String representing the name of the event that 
4573 * was fired.
4574 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4575 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4576 * fired the event.
4577 */
4578 configContainer: function (p_sType, p_aArgs, p_oMenu) {
4579
4580     var oElement = p_aArgs[0];
4581
4582     if (Lang.isString(oElement)) {
4583
4584         this.cfg.setProperty(_CONTAINER, Dom.get(oElement), true);
4585
4586     }
4587
4588 },
4589
4590
4591 /**
4592 * @method _clearSetWidthFlag
4593 * @description Change event listener for the "width" configuration property.  This listener is 
4594 * added when a Menu's "width" configuration property is set by the "_setScrollHeight" method, and 
4595 * is used to set the "_widthSetForScroll" property to "false" if the "width" configuration property 
4596 * is changed after it was set by the "_setScrollHeight" method.  If the "_widthSetForScroll" 
4597 * property is set to "false", and the "_setScrollHeight" method is in the process of tearing down 
4598 * scrolling functionality, it will maintain the Menu's new width rather than reseting it.
4599 * @private
4600 */
4601 _clearSetWidthFlag: function () {
4602
4603     this._widthSetForScroll = false;
4604     
4605     this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
4606
4607 },
4608
4609 /**
4610  * @method _subscribeScrollHandlers
4611  * @param {HTMLElement} oHeader The scroll header element
4612  * @param {HTMLElement} oFooter The scroll footer element
4613  */
4614 _subscribeScrollHandlers : function(oHeader, oFooter) {
4615     var fnMouseOver = this._onScrollTargetMouseOver;
4616     var fnMouseOut = this._onScrollTargetMouseOut;
4617
4618     Event.on(oHeader, _MOUSEOVER, fnMouseOver, this, true);
4619     Event.on(oHeader, _MOUSEOUT, fnMouseOut, this, true);
4620     Event.on(oFooter, _MOUSEOVER, fnMouseOver, this, true);
4621     Event.on(oFooter, _MOUSEOUT, fnMouseOut, this, true);
4622 },
4623
4624 /**
4625  * @method _unsubscribeScrollHandlers 
4626  * @param {HTMLElement} oHeader The scroll header element
4627  * @param {HTMLElement} oFooter The scroll footer element
4628  */
4629 _unsubscribeScrollHandlers : function(oHeader, oFooter) {
4630     var fnMouseOver = this._onScrollTargetMouseOver;
4631     var fnMouseOut = this._onScrollTargetMouseOut;
4632     
4633     Event.removeListener(oHeader, _MOUSEOVER, fnMouseOver);
4634     Event.removeListener(oHeader, _MOUSEOUT, fnMouseOut);
4635     Event.removeListener(oFooter, _MOUSEOVER, fnMouseOver);
4636     Event.removeListener(oFooter, _MOUSEOUT, fnMouseOut);
4637 },
4638
4639 /**
4640 * @method _setScrollHeight
4641 * @description 
4642 * @param {String} p_nScrollHeight Number representing the scrolling height of the Menu.
4643 * @private
4644 */
4645 _setScrollHeight: function (p_nScrollHeight) {
4646
4647     var nScrollHeight = p_nScrollHeight,
4648         bRefireIFrameAndShadow = false,
4649         bSetWidth = false,
4650         oElement,
4651         oBody,
4652         oHeader,
4653         oFooter,
4654         nMinScrollHeight,
4655         nHeight,
4656         nOffsetWidth,
4657         sWidth;
4658
4659     if (this.getItems().length > 0) {
4660
4661         oElement = this.element;
4662         oBody = this.body;
4663         oHeader = this.header;
4664         oFooter = this.footer;
4665         nMinScrollHeight = this.cfg.getProperty(_MIN_SCROLL_HEIGHT);
4666
4667         if (nScrollHeight > 0 && nScrollHeight < nMinScrollHeight) {
4668             nScrollHeight = nMinScrollHeight;
4669         }
4670
4671         Dom.setStyle(oBody, _HEIGHT, _EMPTY_STRING);
4672         Dom.removeClass(oBody, _YUI_MENU_BODY_SCROLLED);
4673         oBody.scrollTop = 0;
4674
4675         //      Need to set a width for the Menu to fix the following problems in 
4676         //      Firefox 2 and IE:
4677
4678         //      #1) Scrolled Menus will render at 1px wide in Firefox 2
4679
4680         //      #2) There is a bug in gecko-based browsers where an element whose 
4681         //      "position" property is set to "absolute" and "overflow" property is 
4682         //      set to "hidden" will not render at the correct width when its 
4683         //      offsetParent's "position" property is also set to "absolute."  It is 
4684         //      possible to work around this bug by specifying a value for the width 
4685         //      property in addition to overflow.
4686
4687         //      #3) In IE it is necessary to give the Menu a width before the 
4688         //      scrollbars are rendered to prevent the Menu from rendering with a 
4689         //      width that is 100% of the browser viewport.
4690
4691         bSetWidth = ((UA.gecko && UA.gecko < 1.9) || UA.ie);
4692
4693         if (nScrollHeight > 0 && bSetWidth && !this.cfg.getProperty(_WIDTH)) {
4694
4695             nOffsetWidth = oElement.offsetWidth;
4696     
4697             /*
4698                 Measuring the difference of the offsetWidth before and after
4699                 setting the "width" style attribute allows us to compute the 
4700                 about of padding and borders applied to the element, which in 
4701                 turn allows us to set the "width" property correctly.
4702             */
4703             
4704             oElement.style.width = nOffsetWidth + _PX;
4705     
4706             sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX;
4707
4708
4709             this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
4710
4711
4712             this.cfg.setProperty(_WIDTH, sWidth);
4713
4714
4715             /*
4716                 Set a flag (_widthSetForScroll) to maintain some history regarding how the 
4717                 "width" configuration property was set.  If the "width" configuration property 
4718                 is set by something other than the "_setScrollHeight" method, it will be 
4719                 necessary to maintain that new value and not clear the width if scrolling 
4720                 is turned off.
4721             */
4722
4723             this._widthSetForScroll = true;
4724
4725             this.cfg.subscribeToConfigEvent(_WIDTH, this._clearSetWidthFlag);
4726     
4727         }
4728
4729
4730         if (nScrollHeight > 0 && (!oHeader && !oFooter)) {
4731
4732
4733             this.setHeader(_NON_BREAKING_SPACE);
4734             this.setFooter(_NON_BREAKING_SPACE);
4735
4736             oHeader = this.header;
4737             oFooter = this.footer;
4738
4739             Dom.addClass(oHeader, _TOP_SCROLLBAR);
4740             Dom.addClass(oFooter, _BOTTOM_SCROLLBAR);
4741
4742             oElement.insertBefore(oHeader, oBody);
4743             oElement.appendChild(oFooter);
4744         
4745         }
4746
4747         nHeight = nScrollHeight;
4748
4749         if (oHeader && oFooter) {
4750             nHeight = (nHeight - (oHeader.offsetHeight + oFooter.offsetHeight));
4751         }
4752     
4753     
4754         if ((nHeight > 0) && (oBody.offsetHeight > nScrollHeight)) {
4755
4756     
4757             Dom.addClass(oBody, _YUI_MENU_BODY_SCROLLED);
4758             Dom.setStyle(oBody, _HEIGHT, (nHeight + _PX));
4759
4760             if (!this._hasScrollEventHandlers) {
4761                 this._subscribeScrollHandlers(oHeader, oFooter);
4762                 this._hasScrollEventHandlers = true;
4763             }
4764     
4765             this._disableScrollHeader();
4766             this._enableScrollFooter();
4767             
4768             bRefireIFrameAndShadow = true;                      
4769     
4770         }
4771         else if (oHeader && oFooter) {
4772
4773     
4774
4775             /*
4776                 Only clear the the "width" configuration property if it was set the 
4777                 "_setScrollHeight" method and wasn't changed by some other means after it was set.
4778             */  
4779     
4780             if (this._widthSetForScroll) {
4781     
4782
4783                 this._widthSetForScroll = false;
4784
4785                 this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
4786     
4787                 this.cfg.setProperty(_WIDTH, _EMPTY_STRING);
4788             
4789             }
4790     
4791     
4792             this._enableScrollHeader();
4793             this._enableScrollFooter();
4794     
4795             if (this._hasScrollEventHandlers) {
4796                 this._unsubscribeScrollHandlers(oHeader, oFooter);    
4797                 this._hasScrollEventHandlers = false;
4798             }
4799
4800             oElement.removeChild(oHeader);
4801             oElement.removeChild(oFooter);
4802     
4803             this.header = null;
4804             this.footer = null;
4805             
4806             bRefireIFrameAndShadow = true;
4807         
4808         }
4809
4810
4811         if (bRefireIFrameAndShadow) {
4812     
4813             this.cfg.refireEvent(_IFRAME);
4814             this.cfg.refireEvent(_SHADOW);
4815         
4816         }
4817     
4818     }
4819
4820 },
4821
4822
4823 /**
4824 * @method _setMaxHeight
4825 * @description "renderEvent" handler used to defer the setting of the 
4826 * "maxheight" configuration property until the menu is rendered in lazy 
4827 * load scenarios.
4828 * @param {String} p_sType The name of the event that was fired.
4829 * @param {Array} p_aArgs Collection of arguments sent when the event 
4830 * was fired.
4831 * @param {Number} p_nMaxHeight Number representing the value to set for the 
4832 * "maxheight" configuration property.
4833 * @private
4834 */
4835 _setMaxHeight: function (p_sType, p_aArgs, p_nMaxHeight) {
4836
4837     this._setScrollHeight(p_nMaxHeight);
4838     this.renderEvent.unsubscribe(this._setMaxHeight);
4839
4840 },
4841
4842
4843 /**
4844 * @method configMaxHeight
4845 * @description Event handler for when the "maxheight" configuration property of 
4846 * a Menu changes.
4847 * @param {String} p_sType The name of the event that was fired.
4848 * @param {Array} p_aArgs Collection of arguments sent when the event 
4849 * was fired.
4850 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired
4851 * the event.
4852 */
4853 configMaxHeight: function (p_sType, p_aArgs, p_oMenu) {
4854
4855     var nMaxHeight = p_aArgs[0];
4856
4857     if (this.lazyLoad && !this.body && nMaxHeight > 0) {
4858     
4859         this.renderEvent.subscribe(this._setMaxHeight, nMaxHeight, this);
4860
4861     }
4862     else {
4863
4864         this._setScrollHeight(nMaxHeight);
4865     
4866     }
4867
4868 },
4869
4870
4871 /**
4872 * @method configClassName
4873 * @description Event handler for when the "classname" configuration property of 
4874 * a menu changes.
4875 * @param {String} p_sType The name of the event that was fired.
4876 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
4877 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
4878 */
4879 configClassName: function (p_sType, p_aArgs, p_oMenu) {
4880
4881     var sClassName = p_aArgs[0];
4882
4883     if (this._sClassName) {
4884
4885         Dom.removeClass(this.element, this._sClassName);
4886
4887     }
4888
4889     Dom.addClass(this.element, sClassName);
4890     this._sClassName = sClassName;
4891
4892 },
4893
4894
4895 /**
4896 * @method _onItemAdded
4897 * @description "itemadded" event handler for a Menu instance.
4898 * @private
4899 * @param {String} p_sType The name of the event that was fired.
4900 * @param {Array} p_aArgs Collection of arguments sent when the event 
4901 * was fired.
4902 */
4903 _onItemAdded: function (p_sType, p_aArgs) {
4904
4905     var oItem = p_aArgs[0];
4906     
4907     if (oItem) {
4908
4909         oItem.cfg.setProperty(_DISABLED, true);
4910     
4911     }
4912
4913 },
4914
4915
4916 /**
4917 * @method configDisabled
4918 * @description Event handler for when the "disabled" configuration property of 
4919 * a menu changes.
4920 * @param {String} p_sType The name of the event that was fired.
4921 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
4922 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
4923 */
4924 configDisabled: function (p_sType, p_aArgs, p_oMenu) {
4925
4926     var bDisabled = p_aArgs[0],
4927         aItems = this.getItems(),
4928         nItems,
4929         i;
4930
4931     if (Lang.isArray(aItems)) {
4932
4933         nItems = aItems.length;
4934     
4935         if (nItems > 0) {
4936         
4937             i = nItems - 1;
4938     
4939             do {
4940     
4941                 aItems[i].cfg.setProperty(_DISABLED, bDisabled);
4942             
4943             }
4944             while (i--);
4945         
4946         }
4947
4948
4949         if (bDisabled) {
4950
4951             this.clearActiveItem(true);
4952
4953             Dom.addClass(this.element, _DISABLED);
4954
4955             this.itemAddedEvent.subscribe(this._onItemAdded);
4956
4957         }
4958         else {
4959
4960             Dom.removeClass(this.element, _DISABLED);
4961
4962             this.itemAddedEvent.unsubscribe(this._onItemAdded);
4963
4964         }
4965         
4966     }
4967
4968 },
4969
4970 /**
4971  * Resizes the shadow to match the container bounding element
4972  * 
4973  * @method _sizeShadow
4974  * @protected
4975  */
4976 _sizeShadow : function () {
4977
4978     var oElement = this.element,
4979         oShadow = this._shadow;
4980
4981     if (oShadow && oElement) {
4982         // Clear the previous width
4983         if (oShadow.style.width && oShadow.style.height) {
4984             oShadow.style.width = _EMPTY_STRING;
4985             oShadow.style.height = _EMPTY_STRING;
4986         }
4987
4988         oShadow.style.width = (oElement.offsetWidth + 6) + _PX;
4989         oShadow.style.height = (oElement.offsetHeight + 1) + _PX;
4990     }
4991 },
4992
4993 /**
4994  * Replaces the shadow element in the DOM with the current shadow element (this._shadow)
4995  * 
4996  * @method _replaceShadow
4997  * @protected 
4998  */
4999 _replaceShadow : function () {
5000     this.element.appendChild(this._shadow);
5001 },
5002
5003 /**
5004  * Adds the classname marker for a visible shadow, to the shadow element
5005  * 
5006  * @method _addShadowVisibleClass
5007  * @protected
5008  */
5009 _addShadowVisibleClass : function () {
5010     Dom.addClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE);
5011 },
5012
5013 /**
5014  * Removes the classname marker for a visible shadow, from the shadow element
5015  * 
5016  * @method _removeShadowVisibleClass
5017  * @protected
5018  */
5019 _removeShadowVisibleClass : function () {
5020     Dom.removeClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE);
5021 },
5022
5023 /**
5024  * Removes the shadow element from the DOM, and unsubscribes all the listeners used to keep it in sync. Used
5025  * to handle setting the shadow to false.
5026  * 
5027  * @method _removeShadow
5028  * @protected
5029  */
5030 _removeShadow : function() {
5031
5032     var p = (this._shadow && this._shadow.parentNode);
5033
5034     if (p) {
5035         p.removeChild(this._shadow);
5036     }
5037
5038     this.beforeShowEvent.unsubscribe(this._addShadowVisibleClass);
5039     this.beforeHideEvent.unsubscribe(this._removeShadowVisibleClass);
5040
5041     this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._sizeShadow);
5042     this.cfg.unsubscribeFromConfigEvent(_HEIGHT, this._sizeShadow);
5043     this.cfg.unsubscribeFromConfigEvent(_MAX_HEIGHT, this._sizeShadow);
5044     this.cfg.unsubscribeFromConfigEvent(_MAX_HEIGHT, this._replaceShadow);
5045
5046     this.changeContentEvent.unsubscribe(this._sizeShadow);
5047
5048     Module.textResizeEvent.unsubscribe(this._sizeShadow);
5049 },
5050
5051 /**
5052  * Used to create the shadow element, add it to the DOM, and subscribe listeners to keep it in sync.
5053  *
5054  * @method _createShadow
5055  * @protected
5056  */
5057 _createShadow : function () {
5058
5059     var oShadow = this._shadow,
5060         oElement;
5061
5062     if (!oShadow) {
5063         oElement = this.element;
5064
5065         if (!m_oShadowTemplate) {
5066             m_oShadowTemplate = document.createElement(_DIV_LOWERCASE);
5067             m_oShadowTemplate.className = _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE;
5068         }
5069
5070         oShadow = m_oShadowTemplate.cloneNode(false);
5071
5072         oElement.appendChild(oShadow);
5073         
5074         this._shadow = oShadow;
5075
5076         this.beforeShowEvent.subscribe(this._addShadowVisibleClass);
5077         this.beforeHideEvent.subscribe(this._removeShadowVisibleClass);
5078
5079         if (UA.ie) {
5080             /*
5081                  Need to call sizeShadow & syncIframe via setTimeout for 
5082                  IE 7 Quirks Mode and IE 6 Standards Mode and Quirks Mode 
5083                  or the shadow and iframe shim will not be sized and 
5084                  positioned properly.
5085             */
5086             Lang.later(0, this, function () {
5087                 this._sizeShadow(); 
5088                 this.syncIframe();
5089             });
5090
5091             this.cfg.subscribeToConfigEvent(_WIDTH, this._sizeShadow);
5092             this.cfg.subscribeToConfigEvent(_HEIGHT, this._sizeShadow);
5093             this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, this._sizeShadow);
5094             this.changeContentEvent.subscribe(this._sizeShadow);
5095
5096             Module.textResizeEvent.subscribe(this._sizeShadow, this, true);
5097
5098             this.destroyEvent.subscribe(function () {
5099                 Module.textResizeEvent.unsubscribe(this._sizeShadow, this);
5100             });
5101         }
5102
5103         this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, this._replaceShadow);
5104     }
5105 },
5106
5107 /**
5108  * The beforeShow event handler used to set up the shadow lazily when the menu is made visible.
5109  * @method _shadowBeforeShow
5110  * @protected 
5111  */
5112 _shadowBeforeShow : function () {
5113     if (this._shadow) {
5114
5115         // If called because the "shadow" event was refired - just append again and resize
5116         this._replaceShadow();
5117
5118         if (UA.ie) {
5119             this._sizeShadow();
5120         }
5121     } else {
5122         this._createShadow();
5123     }
5124
5125     this.beforeShowEvent.unsubscribe(this._shadowBeforeShow);
5126 },
5127
5128 /**
5129 * @method configShadow
5130 * @description Event handler for when the "shadow" configuration property of 
5131 * a menu changes.
5132 * @param {String} p_sType The name of the event that was fired.
5133 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
5134 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
5135 */
5136 configShadow: function (p_sType, p_aArgs, p_oMenu) {
5137
5138     var bShadow = p_aArgs[0];
5139
5140     if (bShadow && this.cfg.getProperty(_POSITION) == _DYNAMIC) {
5141         if (this.cfg.getProperty(_VISIBLE)) {
5142             if (this._shadow) {
5143                 // If the "shadow" event was refired - just append again and resize
5144                 this._replaceShadow();
5145                 
5146                 if (UA.ie) {
5147                     this._sizeShadow();
5148                 }
5149             } else {
5150                 this._createShadow();
5151             }
5152         } else {
5153             this.beforeShowEvent.subscribe(this._shadowBeforeShow);
5154         }
5155     } else if (!bShadow) {
5156         this.beforeShowEvent.unsubscribe(this._shadowBeforeShow);
5157         this._removeShadow();
5158     }
5159 },
5160
5161 // Public methods
5162
5163 /**
5164 * @method initEvents
5165 * @description Initializes the custom events for the menu.
5166 */
5167 initEvents: function () {
5168
5169     Menu.superclass.initEvents.call(this);
5170
5171     // Create custom events
5172
5173     var i = EVENT_TYPES.length - 1,
5174         aEventData,
5175         oCustomEvent;
5176
5177
5178     do {
5179
5180         aEventData = EVENT_TYPES[i];
5181
5182         oCustomEvent = this.createEvent(aEventData[1]);
5183         oCustomEvent.signature = CustomEvent.LIST;
5184         
5185         this[aEventData[0]] = oCustomEvent;
5186
5187     }
5188     while (i--);
5189
5190 },
5191
5192
5193 /**
5194 * @method positionOffScreen
5195 * @description Positions the menu outside of the boundaries of the browser's 
5196 * viewport.  Called automatically when a menu is hidden to ensure that 
5197 * it doesn't force the browser to render uncessary scrollbars.
5198 */
5199 positionOffScreen: function () {
5200
5201     var oIFrame = this.iframe,
5202         oElement = this.element,
5203         sPos = this.OFF_SCREEN_POSITION;
5204     
5205     oElement.style.top = _EMPTY_STRING;
5206     oElement.style.left = _EMPTY_STRING;
5207     
5208     if (oIFrame) {
5209
5210         oIFrame.style.top = sPos;
5211         oIFrame.style.left = sPos;
5212     
5213     }
5214
5215 },
5216
5217
5218 /**
5219 * @method getRoot
5220 * @description Finds the menu's root menu.
5221 */
5222 getRoot: function () {
5223
5224     var oItem = this.parent,
5225         oParentMenu,
5226         returnVal;
5227
5228     if (oItem) {
5229
5230         oParentMenu = oItem.parent;
5231
5232         returnVal = oParentMenu ? oParentMenu.getRoot() : this;
5233
5234     }
5235     else {
5236     
5237         returnVal = this;
5238     
5239     }
5240     
5241     return returnVal;
5242
5243 },
5244
5245
5246 /**
5247 * @method toString
5248 * @description Returns a string representing the menu.
5249 * @return {String}
5250 */
5251 toString: function () {
5252
5253     var sReturnVal = _MENU,
5254         sId = this.id;
5255
5256     if (sId) {
5257
5258         sReturnVal += (_SPACE + sId);
5259     
5260     }
5261
5262     return sReturnVal;
5263
5264 },
5265
5266
5267 /**
5268 * @method setItemGroupTitle
5269 * @description Sets the title of a group of menu items.
5270 * @param {HTML} p_sGroupTitle String or markup specifying the title of the group. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5271 * @param {Number} p_nGroupIndex Optional. Number specifying the group to which
5272 * the title belongs.
5273 */
5274 setItemGroupTitle: function (p_sGroupTitle, p_nGroupIndex) {
5275
5276     var nGroupIndex,
5277         oTitle,
5278         i,
5279         nFirstIndex;
5280         
5281     if (Lang.isString(p_sGroupTitle) && p_sGroupTitle.length > 0) {
5282
5283         nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0;
5284         oTitle = this._aGroupTitleElements[nGroupIndex];
5285
5286
5287         if (oTitle) {
5288
5289             oTitle.innerHTML = p_sGroupTitle;
5290             
5291         }
5292         else {
5293
5294             oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME);
5295                     
5296             oTitle.innerHTML = p_sGroupTitle;
5297
5298             this._aGroupTitleElements[nGroupIndex] = oTitle;
5299
5300         }
5301
5302
5303         i = this._aGroupTitleElements.length - 1;
5304
5305         do {
5306
5307             if (this._aGroupTitleElements[i]) {
5308
5309                 Dom.removeClass(this._aGroupTitleElements[i], _FIRST_OF_TYPE);
5310
5311                 nFirstIndex = i;
5312
5313             }
5314
5315         }
5316         while (i--);
5317
5318
5319         if (nFirstIndex !== null) {
5320
5321             Dom.addClass(this._aGroupTitleElements[nFirstIndex], 
5322                 _FIRST_OF_TYPE);
5323
5324         }
5325
5326         this.changeContentEvent.fire();
5327
5328     }
5329
5330 },
5331
5332
5333
5334 /**
5335 * @method addItem
5336 * @description Appends an item to the menu.
5337 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
5338 * instance to be added to the menu.
5339 * @param {HTML} p_oItem String or markup specifying content of the item to be added 
5340 * to the menu. The item text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5341 * @param {Object} p_oItem Object literal containing a set of menu item 
5342 * configuration properties.
5343 * @param {Number} p_nGroupIndex Optional. Number indicating the group to
5344 * which the item belongs.
5345 * @return {YAHOO.widget.MenuItem}
5346 */
5347 addItem: function (p_oItem, p_nGroupIndex) {
5348
5349     return this._addItemToGroup(p_nGroupIndex, p_oItem);
5350
5351 },
5352
5353
5354 /**
5355 * @method addItems
5356 * @description Adds an array of items to the menu.
5357 * @param {Array} p_aItems Array of items to be added to the menu.  The array 
5358 * can contain strings specifying the markup for the content of each item to be created, object
5359 * literals specifying each of the menu item configuration properties, 
5360 * or MenuItem instances. The item content if provided as a string is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5361 * @param {Number} p_nGroupIndex Optional. Number specifying the group to 
5362 * which the items belongs.
5363 * @return {Array}
5364 */
5365 addItems: function (p_aItems, p_nGroupIndex) {
5366
5367     var nItems,
5368         aItems,
5369         oItem,
5370         i,
5371         returnVal;
5372
5373
5374     if (Lang.isArray(p_aItems)) {
5375
5376         nItems = p_aItems.length;
5377         aItems = [];
5378
5379         for(i=0; i<nItems; i++) {
5380
5381             oItem = p_aItems[i];
5382
5383             if (oItem) {
5384
5385                 if (Lang.isArray(oItem)) {
5386     
5387                     aItems[aItems.length] = this.addItems(oItem, i);
5388     
5389                 }
5390                 else {
5391     
5392                     aItems[aItems.length] = this._addItemToGroup(p_nGroupIndex, oItem);
5393                 
5394                 }
5395
5396             }
5397     
5398         }
5399
5400
5401         if (aItems.length) {
5402         
5403             returnVal = aItems;
5404         
5405         }
5406
5407     }
5408
5409     return returnVal;
5410
5411 },
5412
5413
5414 /**
5415 * @method insertItem
5416 * @description Inserts an item into the menu at the specified index.
5417 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
5418 * instance to be added to the menu.
5419 * @param {String} p_oItem String specifying the text of the item to be added 
5420 * to the menu.
5421 * @param {Object} p_oItem Object literal containing a set of menu item 
5422 * configuration properties.
5423 * @param {Number} p_nItemIndex Number indicating the ordinal position at which
5424 * the item should be added.
5425 * @param {Number} p_nGroupIndex Optional. Number indicating the group to which 
5426 * the item belongs.
5427 * @return {YAHOO.widget.MenuItem}
5428 */
5429 insertItem: function (p_oItem, p_nItemIndex, p_nGroupIndex) {
5430     
5431     return this._addItemToGroup(p_nGroupIndex, p_oItem, p_nItemIndex);
5432
5433 },
5434
5435
5436 /**
5437 * @method removeItem
5438 * @description Removes the specified item from the menu.
5439 * @param {YAHOO.widget.MenuItem} p_oObject Object reference for the MenuItem 
5440 * instance to be removed from the menu.
5441 * @param {Number} p_oObject Number specifying the index of the item 
5442 * to be removed.
5443 * @param {Number} p_nGroupIndex Optional. Number specifying the group to 
5444 * which the item belongs.
5445 * @return {YAHOO.widget.MenuItem}
5446 */
5447 removeItem: function (p_oObject, p_nGroupIndex) {
5448
5449     var oItem,
5450         returnVal;
5451     
5452     if (!Lang.isUndefined(p_oObject)) {
5453
5454         if (p_oObject instanceof YAHOO.widget.MenuItem) {
5455
5456             oItem = this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject);           
5457
5458         }
5459         else if (Lang.isNumber(p_oObject)) {
5460
5461             oItem = this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject);
5462
5463         }
5464
5465         if (oItem) {
5466
5467             oItem.destroy();
5468
5469
5470             returnVal = oItem;
5471
5472         }
5473
5474     }
5475
5476     return returnVal;
5477
5478 },
5479
5480
5481 /**
5482 * @method getItems
5483 * @description Returns an array of all of the items in the menu.
5484 * @return {Array}
5485 */
5486 getItems: function () {
5487
5488     var aGroups = this._aItemGroups,
5489         nGroups,
5490         returnVal,
5491         aItems = [];
5492
5493
5494     if (Lang.isArray(aGroups)) {
5495
5496         nGroups = aGroups.length;
5497
5498         returnVal = ((nGroups == 1) ? aGroups[0] : (Array.prototype.concat.apply(aItems, aGroups)));
5499
5500     }
5501
5502     return returnVal;
5503
5504 },
5505
5506
5507 /**
5508 * @method getItemGroups
5509 * @description Multi-dimensional Array representing the menu items as they 
5510 * are grouped in the menu.
5511 * @return {Array}
5512 */        
5513 getItemGroups: function () {
5514
5515     return this._aItemGroups;
5516
5517 },
5518
5519
5520 /**
5521 * @method getItem
5522 * @description Returns the item at the specified index.
5523 * @param {Number} p_nItemIndex Number indicating the ordinal position of the 
5524 * item to be retrieved.
5525 * @param {Number} p_nGroupIndex Optional. Number indicating the group to which 
5526 * the item belongs.
5527 * @return {YAHOO.widget.MenuItem}
5528 */
5529 getItem: function (p_nItemIndex, p_nGroupIndex) {
5530     
5531     var aGroup,
5532         returnVal;
5533     
5534     if (Lang.isNumber(p_nItemIndex)) {
5535
5536         aGroup = this._getItemGroup(p_nGroupIndex);
5537
5538         if (aGroup) {
5539
5540             returnVal = aGroup[p_nItemIndex];
5541         
5542         }
5543
5544     }
5545     
5546     return returnVal;
5547     
5548 },
5549
5550
5551 /**
5552 * @method getSubmenus
5553 * @description Returns an array of all of the submenus that are immediate 
5554 * children of the menu.
5555 * @return {Array}
5556 */
5557 getSubmenus: function () {
5558
5559     var aItems = this.getItems(),
5560         nItems = aItems.length,
5561         aSubmenus,
5562         oSubmenu,
5563         oItem,
5564         i;
5565
5566
5567     if (nItems > 0) {
5568         
5569         aSubmenus = [];
5570
5571         for(i=0; i<nItems; i++) {
5572
5573             oItem = aItems[i];
5574             
5575             if (oItem) {
5576
5577                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5578                 
5579                 if (oSubmenu) {
5580
5581                     aSubmenus[aSubmenus.length] = oSubmenu;
5582
5583                 }
5584             
5585             }
5586         
5587         }
5588     
5589     }
5590
5591     return aSubmenus;
5592
5593 },
5594
5595
5596 /**
5597 * @method clearContent
5598 * @description Removes all of the content from the menu, including the menu 
5599 * items, group titles, header and footer.
5600 */
5601 clearContent: function () {
5602
5603     var aItems = this.getItems(),
5604         nItems = aItems.length,
5605         oElement = this.element,
5606         oBody = this.body,
5607         oHeader = this.header,
5608         oFooter = this.footer,
5609         oItem,
5610         oSubmenu,
5611         i;
5612
5613
5614     if (nItems > 0) {
5615
5616         i = nItems - 1;
5617
5618         do {
5619
5620             oItem = aItems[i];
5621
5622             if (oItem) {
5623
5624                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5625
5626                 if (oSubmenu) {
5627
5628                     this.cfg.configChangedEvent.unsubscribe(
5629                         this._onParentMenuConfigChange, oSubmenu);
5630
5631                     this.renderEvent.unsubscribe(this._onParentMenuRender, 
5632                         oSubmenu);
5633
5634                 }
5635                 
5636                 this.removeItem(oItem, oItem.groupIndex);
5637
5638             }
5639         
5640         }
5641         while (i--);
5642
5643     }
5644
5645
5646     if (oHeader) {
5647
5648         Event.purgeElement(oHeader);
5649         oElement.removeChild(oHeader);
5650
5651     }
5652     
5653
5654     if (oFooter) {
5655
5656         Event.purgeElement(oFooter);
5657         oElement.removeChild(oFooter);
5658     }
5659
5660
5661     if (oBody) {
5662
5663         Event.purgeElement(oBody);
5664
5665         oBody.innerHTML = _EMPTY_STRING;
5666
5667     }
5668
5669     this.activeItem = null;
5670
5671     this._aItemGroups = [];
5672     this._aListElements = [];
5673     this._aGroupTitleElements = [];
5674
5675     this.cfg.setProperty(_WIDTH, null);
5676
5677 },
5678
5679
5680 /**
5681 * @method destroy
5682 * @description Removes the menu's <code>&#60;div&#62;</code> element 
5683 * (and accompanying child nodes) from the document.
5684 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 
5685 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0.
5686
5687 */
5688 destroy: function (shallowPurge) {
5689
5690     // Remove all items
5691
5692     this.clearContent();
5693
5694     this._aItemGroups = null;
5695     this._aListElements = null;
5696     this._aGroupTitleElements = null;
5697
5698
5699     // Continue with the superclass implementation of this method
5700
5701     Menu.superclass.destroy.call(this, shallowPurge);
5702     
5703
5704 },
5705
5706
5707 /**
5708 * @method setInitialFocus
5709 * @description Sets focus to the menu's first enabled item.
5710 */
5711 setInitialFocus: function () {
5712
5713     var oItem = this._getFirstEnabledItem();
5714     
5715     if (oItem) {
5716
5717         oItem.focus();
5718
5719     }
5720     
5721 },
5722
5723
5724 /**
5725 * @method setInitialSelection
5726 * @description Sets the "selected" configuration property of the menu's first 
5727 * enabled item to "true."
5728 */
5729 setInitialSelection: function () {
5730
5731     var oItem = this._getFirstEnabledItem();
5732     
5733     if (oItem) {
5734     
5735         oItem.cfg.setProperty(_SELECTED, true);
5736     }        
5737
5738 },
5739
5740
5741 /**
5742 * @method clearActiveItem
5743 * @description Sets the "selected" configuration property of the menu's active
5744 * item to "false" and hides the item's submenu.
5745 * @param {Boolean} p_bBlur Boolean indicating if the menu's active item 
5746 * should be blurred.  
5747 */
5748 clearActiveItem: function (p_bBlur) {
5749
5750     if (this.cfg.getProperty(_SHOW_DELAY) > 0) {
5751     
5752         this._cancelShowDelay();
5753     
5754     }
5755
5756
5757     var oActiveItem = this.activeItem,
5758         oConfig,
5759         oSubmenu;
5760
5761     if (oActiveItem) {
5762
5763         oConfig = oActiveItem.cfg;
5764
5765         if (p_bBlur) {
5766
5767             oActiveItem.blur();
5768             
5769             this.getRoot()._hasFocus = true;
5770         
5771         }
5772
5773         oConfig.setProperty(_SELECTED, false);
5774
5775         oSubmenu = oConfig.getProperty(_SUBMENU);
5776
5777
5778         if (oSubmenu) {
5779
5780             oSubmenu.hide();
5781
5782         }
5783
5784         this.activeItem = null;  
5785
5786     }
5787
5788 },
5789
5790
5791 /**
5792 * @method focus
5793 * @description Causes the menu to receive focus and fires the "focus" event.
5794 */
5795 focus: function () {
5796
5797     if (!this.hasFocus()) {
5798
5799         this.setInitialFocus();
5800     
5801     }
5802
5803 },
5804
5805
5806 /**
5807 * @method blur
5808 * @description Causes the menu to lose focus and fires the "blur" event.
5809 */    
5810 blur: function () {
5811
5812     var oItem;
5813
5814     if (this.hasFocus()) {
5815     
5816         oItem = MenuManager.getFocusedMenuItem();
5817         
5818         if (oItem) {
5819
5820             oItem.blur();
5821
5822         }
5823
5824     }
5825
5826 },
5827
5828
5829 /**
5830 * @method hasFocus
5831 * @description Returns a boolean indicating whether or not the menu has focus.
5832 * @return {Boolean}
5833 */
5834 hasFocus: function () {
5835
5836     return (MenuManager.getFocusedMenu() == this.getRoot());
5837
5838 },
5839
5840
5841 _doItemSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) {
5842
5843     var oItem = p_aArgs[0],
5844         oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5845
5846     if (oSubmenu) {
5847         oSubmenu.subscribe.apply(oSubmenu, p_oObject);
5848     }
5849
5850 },
5851
5852
5853 _doSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) { 
5854
5855     var oSubmenu = this.cfg.getProperty(_SUBMENU);
5856     
5857     if (oSubmenu) {
5858         oSubmenu.subscribe.apply(oSubmenu, p_oObject);
5859     }
5860
5861 },
5862
5863
5864 /**
5865 * Adds the specified CustomEvent subscriber to the menu and each of 
5866 * its submenus.
5867 * @method subscribe
5868 * @param p_type     {string}   the type, or name of the event
5869 * @param p_fn       {function} the function to exectute when the event fires
5870 * @param p_obj      {Object}   An object to be passed along when the event 
5871 *                              fires
5872 * @param p_override {boolean}  If true, the obj passed in becomes the 
5873 *                              execution scope of the listener
5874 */
5875 subscribe: function () {
5876
5877     //  Subscribe to the event for this Menu instance
5878     Menu.superclass.subscribe.apply(this, arguments);
5879
5880     //  Subscribe to the "itemAdded" event so that all future submenus
5881     //  also subscribe to this event
5882     Menu.superclass.subscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments);
5883
5884
5885     var aItems = this.getItems(),
5886         nItems,
5887         oItem,
5888         oSubmenu,
5889         i;
5890         
5891
5892     if (aItems) {
5893
5894         nItems = aItems.length;
5895         
5896         if (nItems > 0) {
5897         
5898             i = nItems - 1;
5899             
5900             do {
5901
5902                 oItem = aItems[i];
5903                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5904                 
5905                 if (oSubmenu) {
5906                     oSubmenu.subscribe.apply(oSubmenu, arguments);
5907                 }
5908                 else {
5909                     oItem.cfg.subscribeToConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments);
5910                 }
5911
5912             }
5913             while (i--);
5914         
5915         }
5916
5917     }
5918
5919 },
5920
5921
5922 unsubscribe: function () {
5923
5924     //  Remove the event for this Menu instance
5925     Menu.superclass.unsubscribe.apply(this, arguments);
5926
5927     //  Remove the "itemAdded" event so that all future submenus don't have 
5928     //  the event handler
5929     Menu.superclass.unsubscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments);
5930
5931
5932     var aItems = this.getItems(),
5933         nItems,
5934         oItem,
5935         oSubmenu,
5936         i;
5937         
5938
5939     if (aItems) {
5940
5941         nItems = aItems.length;
5942         
5943         if (nItems > 0) {
5944         
5945             i = nItems - 1;
5946             
5947             do {
5948
5949                 oItem = aItems[i];
5950                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5951                 
5952                 if (oSubmenu) {
5953                     oSubmenu.unsubscribe.apply(oSubmenu, arguments);
5954                 }
5955                 else {
5956                     oItem.cfg.unsubscribeFromConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments);
5957                 }
5958
5959             }
5960             while (i--);
5961         
5962         }
5963
5964     }
5965
5966 },
5967
5968
5969 /**
5970 * @description Initializes the class's configurable properties which can be
5971 * changed using the menu's Config object ("cfg").
5972 * @method initDefaultConfig
5973 */
5974 initDefaultConfig: function () {
5975
5976     Menu.superclass.initDefaultConfig.call(this);
5977
5978     var oConfig = this.cfg;
5979
5980
5981     // Module documentation overrides
5982
5983     /**
5984     * @config effect
5985     * @description Object or array of objects representing the ContainerEffect 
5986     * classes that are active for animating the container.  When set this 
5987     * property is automatically applied to all submenus.
5988     * @type Object
5989     * @default null
5990     */
5991
5992     // Overlay documentation overrides
5993
5994
5995     /**
5996     * @config x
5997     * @description Number representing the absolute x-coordinate position of 
5998     * the Menu.  This property is only applied when the "position" 
5999     * configuration property is set to dynamic.
6000     * @type Number
6001     * @default null
6002     */
6003     
6004
6005     /**
6006     * @config y
6007     * @description Number representing the absolute y-coordinate position of 
6008     * the Menu.  This property is only applied when the "position" 
6009     * configuration property is set to dynamic.
6010     * @type Number
6011     * @default null
6012     */
6013
6014
6015     /**
6016     * @description Array of the absolute x and y positions of the Menu.  This 
6017     * property is only applied when the "position" configuration property is 
6018     * set to dynamic.
6019     * @config xy
6020     * @type Number[]
6021     * @default null
6022     */
6023     
6024
6025     /**
6026     * @config context
6027     * @description Array of context arguments for context-sensitive positioning.  
6028     * The format is: [id or element, element corner, context corner]. 
6029     * For example, setting this property to ["img1", "tl", "bl"] would 
6030     * align the Menu's top left corner to the context element's 
6031     * bottom left corner.  This property is only applied when the "position" 
6032     * configuration property is set to dynamic.
6033     * @type Array
6034     * @default null
6035     */
6036     
6037     
6038     /**
6039     * @config fixedcenter
6040     * @description Boolean indicating if the Menu should be anchored to the 
6041     * center of the viewport.  This property is only applied when the 
6042     * "position" configuration property is set to dynamic.
6043     * @type Boolean
6044     * @default false
6045     */
6046     
6047     
6048     /**
6049     * @config iframe
6050     * @description Boolean indicating whether or not the Menu should 
6051     * have an IFRAME shim; used to prevent SELECT elements from 
6052     * poking through an Overlay instance in IE6.  When set to "true", 
6053     * the iframe shim is created when the Menu instance is intially
6054     * made visible.  This property is only applied when the "position" 
6055     * configuration property is set to dynamic and is automatically applied 
6056     * to all submenus.
6057     * @type Boolean
6058     * @default true for IE6 and below, false for all other browsers.
6059     */
6060
6061
6062     // Add configuration attributes
6063
6064     /*
6065         Change the default value for the "visible" configuration 
6066         property to "false" by re-adding the property.
6067     */
6068
6069     /**
6070     * @config visible
6071     * @description Boolean indicating whether or not the menu is visible.  If 
6072     * the menu's "position" configuration property is set to "dynamic" (the 
6073     * default), this property toggles the menu's <code>&#60;div&#62;</code> 
6074     * element's "visibility" style property between "visible" (true) or 
6075     * "hidden" (false).  If the menu's "position" configuration property is 
6076     * set to "static" this property toggles the menu's 
6077     * <code>&#60;div&#62;</code> element's "display" style property 
6078     * between "block" (true) or "none" (false).
6079     * @default false
6080     * @type Boolean
6081     */
6082     oConfig.addProperty(
6083         VISIBLE_CONFIG.key, 
6084         {
6085             handler: this.configVisible, 
6086             value: VISIBLE_CONFIG.value, 
6087             validator: VISIBLE_CONFIG.validator
6088         }
6089      );
6090
6091
6092     /*
6093         Change the default value for the "constraintoviewport" configuration 
6094         property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property.
6095     */
6096
6097     /**
6098     * @config constraintoviewport
6099     * @description Boolean indicating if the menu will try to remain inside 
6100     * the boundaries of the size of viewport.  This property is only applied 
6101     * when the "position" configuration property is set to dynamic and is 
6102     * automatically applied to all submenus.
6103     * @default true
6104     * @type Boolean
6105     */
6106     oConfig.addProperty(
6107         CONSTRAIN_TO_VIEWPORT_CONFIG.key, 
6108         {
6109             handler: this.configConstrainToViewport, 
6110             value: CONSTRAIN_TO_VIEWPORT_CONFIG.value, 
6111             validator: CONSTRAIN_TO_VIEWPORT_CONFIG.validator, 
6112             supercedes: CONSTRAIN_TO_VIEWPORT_CONFIG.supercedes 
6113         } 
6114     );
6115
6116
6117     /*
6118         Change the default value for the "preventcontextoverlap" configuration 
6119         property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property.
6120     */
6121
6122     /**
6123     * @config preventcontextoverlap
6124     * @description Boolean indicating whether or not a submenu should overlap its parent MenuItem 
6125     * when the "constraintoviewport" configuration property is set to "true".
6126     * @type Boolean
6127     * @default true
6128     */
6129     oConfig.addProperty(PREVENT_CONTEXT_OVERLAP_CONFIG.key, {
6130
6131         value: PREVENT_CONTEXT_OVERLAP_CONFIG.value, 
6132         validator: PREVENT_CONTEXT_OVERLAP_CONFIG.validator, 
6133         supercedes: PREVENT_CONTEXT_OVERLAP_CONFIG.supercedes
6134
6135     });
6136
6137
6138     /**
6139     * @config position
6140     * @description String indicating how a menu should be positioned on the 
6141     * screen.  Possible values are "static" and "dynamic."  Static menus are 
6142     * visible by default and reside in the normal flow of the document 
6143     * (CSS position: static).  Dynamic menus are hidden by default, reside 
6144     * out of the normal flow of the document (CSS position: absolute), and 
6145     * can overlay other elements on the screen.
6146     * @default dynamic
6147     * @type String
6148     */
6149     oConfig.addProperty(
6150         POSITION_CONFIG.key, 
6151         {
6152             handler: this.configPosition,
6153             value: POSITION_CONFIG.value, 
6154             validator: POSITION_CONFIG.validator,
6155             supercedes: POSITION_CONFIG.supercedes
6156         }
6157     );
6158
6159
6160     /**
6161     * @config submenualignment
6162     * @description Array defining how submenus should be aligned to their 
6163     * parent menu item. The format is: [itemCorner, submenuCorner]. By default
6164     * a submenu's top left corner is aligned to its parent menu item's top 
6165     * right corner.
6166     * @default ["tl","tr"]
6167     * @type Array
6168     */
6169     oConfig.addProperty(
6170         SUBMENU_ALIGNMENT_CONFIG.key, 
6171         { 
6172             value: SUBMENU_ALIGNMENT_CONFIG.value,
6173             suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent
6174         }
6175     );
6176
6177
6178     /**
6179     * @config autosubmenudisplay
6180     * @description Boolean indicating if submenus are automatically made 
6181     * visible when the user mouses over the menu's items.
6182     * @default true
6183     * @type Boolean
6184     */
6185     oConfig.addProperty(
6186        AUTO_SUBMENU_DISPLAY_CONFIG.key, 
6187        { 
6188            value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 
6189            validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator,
6190            suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent
6191        } 
6192     );
6193
6194
6195     /**
6196     * @config showdelay
6197     * @description Number indicating the time (in milliseconds) that should 
6198     * expire before a submenu is made visible when the user mouses over 
6199     * the menu's items.  This property is only applied when the "position" 
6200     * configuration property is set to dynamic and is automatically applied 
6201     * to all submenus.
6202     * @default 250
6203     * @type Number
6204     */
6205     oConfig.addProperty(
6206        SHOW_DELAY_CONFIG.key, 
6207        { 
6208            value: SHOW_DELAY_CONFIG.value, 
6209            validator: SHOW_DELAY_CONFIG.validator,
6210            suppressEvent: SHOW_DELAY_CONFIG.suppressEvent
6211        } 
6212     );
6213
6214
6215     /**
6216     * @config hidedelay
6217     * @description Number indicating the time (in milliseconds) that should 
6218     * expire before the menu is hidden.  This property is only applied when 
6219     * the "position" configuration property is set to dynamic and is 
6220     * automatically applied to all submenus.
6221     * @default 0
6222     * @type Number
6223     */
6224     oConfig.addProperty(
6225        HIDE_DELAY_CONFIG.key, 
6226        { 
6227            handler: this.configHideDelay,
6228            value: HIDE_DELAY_CONFIG.value, 
6229            validator: HIDE_DELAY_CONFIG.validator, 
6230            suppressEvent: HIDE_DELAY_CONFIG.suppressEvent
6231        } 
6232     );
6233
6234
6235     /**
6236     * @config submenuhidedelay
6237     * @description Number indicating the time (in milliseconds) that should 
6238     * expire before a submenu is hidden when the user mouses out of a menu item 
6239     * heading in the direction of a submenu.  The value must be greater than or 
6240     * equal to the value specified for the "showdelay" configuration property.
6241     * This property is only applied when the "position" configuration property 
6242     * is set to dynamic and is automatically applied to all submenus.
6243     * @default 250
6244     * @type Number
6245     */
6246     oConfig.addProperty(
6247        SUBMENU_HIDE_DELAY_CONFIG.key, 
6248        { 
6249            value: SUBMENU_HIDE_DELAY_CONFIG.value, 
6250            validator: SUBMENU_HIDE_DELAY_CONFIG.validator,
6251            suppressEvent: SUBMENU_HIDE_DELAY_CONFIG.suppressEvent
6252        } 
6253     );
6254
6255
6256     /**
6257     * @config clicktohide
6258     * @description Boolean indicating if the menu will automatically be 
6259     * hidden if the user clicks outside of it.  This property is only 
6260     * applied when the "position" configuration property is set to dynamic 
6261     * and is automatically applied to all submenus.
6262     * @default true
6263     * @type Boolean
6264     */
6265     oConfig.addProperty(
6266         CLICK_TO_HIDE_CONFIG.key,
6267         {
6268             value: CLICK_TO_HIDE_CONFIG.value,
6269             validator: CLICK_TO_HIDE_CONFIG.validator,
6270             suppressEvent: CLICK_TO_HIDE_CONFIG.suppressEvent
6271         }
6272     );
6273
6274
6275     /**
6276     * @config container
6277     * @description HTML element reference or string specifying the id 
6278     * attribute of the HTML element that the menu's markup should be 
6279     * rendered into.
6280     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
6281     * level-one-html.html#ID-58190037">HTMLElement</a>|String
6282     * @default document.body
6283     */
6284     oConfig.addProperty(
6285        CONTAINER_CONFIG.key, 
6286        { 
6287            handler: this.configContainer,
6288            value: document.body,
6289            suppressEvent: CONTAINER_CONFIG.suppressEvent
6290        } 
6291    );
6292
6293
6294     /**
6295     * @config scrollincrement
6296     * @description Number used to control the scroll speed of a menu.  Used to 
6297     * increment the "scrollTop" property of the menu's body by when a menu's 
6298     * content is scrolling.  When set this property is automatically applied 
6299     * to all submenus.
6300     * @default 1
6301     * @type Number
6302     */
6303     oConfig.addProperty(
6304         SCROLL_INCREMENT_CONFIG.key, 
6305         { 
6306             value: SCROLL_INCREMENT_CONFIG.value, 
6307             validator: SCROLL_INCREMENT_CONFIG.validator,
6308             supercedes: SCROLL_INCREMENT_CONFIG.supercedes,
6309             suppressEvent: SCROLL_INCREMENT_CONFIG.suppressEvent
6310         }
6311     );
6312
6313
6314     /**
6315     * @config minscrollheight
6316     * @description Number defining the minimum threshold for the "maxheight" 
6317     * configuration property.  When set this property is automatically applied 
6318     * to all submenus.
6319     * @default 90
6320     * @type Number
6321     */
6322     oConfig.addProperty(
6323         MIN_SCROLL_HEIGHT_CONFIG.key, 
6324         { 
6325             value: MIN_SCROLL_HEIGHT_CONFIG.value, 
6326             validator: MIN_SCROLL_HEIGHT_CONFIG.validator,
6327             supercedes: MIN_SCROLL_HEIGHT_CONFIG.supercedes,
6328             suppressEvent: MIN_SCROLL_HEIGHT_CONFIG.suppressEvent
6329         }
6330     );
6331
6332
6333     /**
6334     * @config maxheight
6335     * @description Number defining the maximum height (in pixels) for a menu's 
6336     * body element (<code>&#60;div class="bd"&#62;</code>).  Once a menu's body 
6337     * exceeds this height, the contents of the body are scrolled to maintain 
6338     * this value.  This value cannot be set lower than the value of the 
6339     * "minscrollheight" configuration property.
6340     * @default 0
6341     * @type Number
6342     */
6343     oConfig.addProperty(
6344        MAX_HEIGHT_CONFIG.key, 
6345        {
6346             handler: this.configMaxHeight,
6347             value: MAX_HEIGHT_CONFIG.value,
6348             validator: MAX_HEIGHT_CONFIG.validator,
6349             suppressEvent: MAX_HEIGHT_CONFIG.suppressEvent,
6350             supercedes: MAX_HEIGHT_CONFIG.supercedes            
6351        } 
6352     );
6353
6354
6355     /**
6356     * @config classname
6357     * @description String representing the CSS class to be applied to the 
6358     * menu's root <code>&#60;div&#62;</code> element.  The specified class(es)  
6359     * are appended in addition to the default class as specified by the menu's
6360     * CSS_CLASS_NAME constant. When set this property is automatically 
6361     * applied to all submenus.
6362     * @default null
6363     * @type String
6364     */
6365     oConfig.addProperty(
6366         CLASS_NAME_CONFIG.key, 
6367         { 
6368             handler: this.configClassName,
6369             value: CLASS_NAME_CONFIG.value, 
6370             validator: CLASS_NAME_CONFIG.validator,
6371             supercedes: CLASS_NAME_CONFIG.supercedes      
6372         }
6373     );
6374
6375
6376     /**
6377     * @config disabled
6378     * @description Boolean indicating if the menu should be disabled.  
6379     * Disabling a menu disables each of its items.  (Disabled menu items are 
6380     * dimmed and will not respond to user input or fire events.)  Disabled
6381     * menus have a corresponding "disabled" CSS class applied to their root
6382     * <code>&#60;div&#62;</code> element.
6383     * @default false
6384     * @type Boolean
6385     */
6386     oConfig.addProperty(
6387         DISABLED_CONFIG.key, 
6388         { 
6389             handler: this.configDisabled,
6390             value: DISABLED_CONFIG.value, 
6391             validator: DISABLED_CONFIG.validator,
6392             suppressEvent: DISABLED_CONFIG.suppressEvent
6393         }
6394     );
6395
6396
6397     /**
6398     * @config shadow
6399     * @description Boolean indicating if the menu should have a shadow.
6400     * @default true
6401     * @type Boolean
6402     */
6403     oConfig.addProperty(
6404         SHADOW_CONFIG.key, 
6405         { 
6406             handler: this.configShadow,
6407             value: SHADOW_CONFIG.value, 
6408             validator: SHADOW_CONFIG.validator
6409         }
6410     );
6411
6412
6413     /**
6414     * @config keepopen
6415     * @description Boolean indicating if the menu should remain open when clicked.
6416     * @default false
6417     * @type Boolean
6418     */
6419     oConfig.addProperty(
6420         KEEP_OPEN_CONFIG.key, 
6421         { 
6422             value: KEEP_OPEN_CONFIG.value, 
6423             validator: KEEP_OPEN_CONFIG.validator
6424         }
6425     );
6426
6427 }
6428
6429 }); // END YAHOO.lang.extend
6430
6431 })();
6432
6433
6434
6435 (function () {
6436
6437 /**
6438 * Creates an item for a menu.
6439
6440 * @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6441 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6442 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 
6443 * the <code>&#60;li&#62;</code> element of the menu item.
6444 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6445 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
6446 * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
6447 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6448 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 
6449 * specifying the <code>&#60;option&#62;</code> element of the menu item.
6450 * @param {Object} p_oConfig Optional. Object literal specifying the 
6451 * configuration for the menu item. See configuration class documentation 
6452 * for more details.
6453 * @class MenuItem
6454 * @constructor
6455 */
6456 YAHOO.widget.MenuItem = function (p_oObject, p_oConfig) {
6457
6458     if (p_oObject) {
6459
6460         if (p_oConfig) {
6461     
6462             this.parent = p_oConfig.parent;
6463             this.value = p_oConfig.value;
6464             this.id = p_oConfig.id;
6465
6466         }
6467
6468         this.init(p_oObject, p_oConfig);
6469
6470     }
6471
6472 };
6473
6474
6475 var Dom = YAHOO.util.Dom,
6476     Module = YAHOO.widget.Module,
6477     Menu = YAHOO.widget.Menu,
6478     MenuItem = YAHOO.widget.MenuItem,
6479     CustomEvent = YAHOO.util.CustomEvent,
6480     UA = YAHOO.env.ua,
6481     Lang = YAHOO.lang,
6482
6483     // Private string constants
6484
6485     _TEXT = "text",
6486     _HASH = "#",
6487     _HYPHEN = "-",
6488     _HELP_TEXT = "helptext",
6489     _URL = "url",
6490     _TARGET = "target",
6491     _EMPHASIS = "emphasis",
6492     _STRONG_EMPHASIS = "strongemphasis",
6493     _CHECKED = "checked",
6494     _SUBMENU = "submenu",
6495     _DISABLED = "disabled",
6496     _SELECTED = "selected",
6497     _HAS_SUBMENU = "hassubmenu",
6498     _CHECKED_DISABLED = "checked-disabled",
6499     _HAS_SUBMENU_DISABLED = "hassubmenu-disabled",
6500     _HAS_SUBMENU_SELECTED = "hassubmenu-selected",
6501     _CHECKED_SELECTED = "checked-selected",
6502     _ONCLICK = "onclick",
6503     _CLASSNAME = "classname",
6504     _EMPTY_STRING = "",
6505     _OPTION = "OPTION",
6506     _OPTGROUP = "OPTGROUP",
6507     _LI_UPPERCASE = "LI",
6508     _HREF = "href",
6509     _SELECT = "SELECT",
6510     _DIV = "DIV",
6511     _START_HELP_TEXT = "<em class=\"helptext\">",
6512     _START_EM = "<em>",
6513     _END_EM = "</em>",
6514     _START_STRONG = "<strong>",
6515     _END_STRONG = "</strong>",
6516     _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
6517     _OBJ = "obj",
6518     _SCOPE = "scope",
6519     _NONE = "none",
6520     _VISIBLE = "visible",
6521     _SPACE = " ",
6522     _MENUITEM = "MenuItem",
6523     _CLICK = "click",
6524     _SHOW = "show",
6525     _HIDE = "hide",
6526     _LI_LOWERCASE = "li",
6527     _ANCHOR_TEMPLATE = "<a href=\"#\"></a>",
6528
6529     EVENT_TYPES = [
6530     
6531         ["mouseOverEvent", "mouseover"],
6532         ["mouseOutEvent", "mouseout"],
6533         ["mouseDownEvent", "mousedown"],
6534         ["mouseUpEvent", "mouseup"],
6535         ["clickEvent", _CLICK],
6536         ["keyPressEvent", "keypress"],
6537         ["keyDownEvent", "keydown"],
6538         ["keyUpEvent", "keyup"],
6539         ["focusEvent", "focus"],
6540         ["blurEvent", "blur"],
6541         ["destroyEvent", "destroy"]
6542     
6543     ],
6544
6545     TEXT_CONFIG = { 
6546         key: _TEXT, 
6547         value: _EMPTY_STRING, 
6548         validator: Lang.isString, 
6549         suppressEvent: true 
6550     }, 
6551
6552     HELP_TEXT_CONFIG = { 
6553         key: _HELP_TEXT,
6554         supercedes: [_TEXT], 
6555         suppressEvent: true 
6556     },
6557
6558     URL_CONFIG = { 
6559         key: _URL, 
6560         value: _HASH, 
6561         suppressEvent: true 
6562     }, 
6563
6564     TARGET_CONFIG = { 
6565         key: _TARGET, 
6566         suppressEvent: true 
6567     }, 
6568
6569     EMPHASIS_CONFIG = { 
6570         key: _EMPHASIS, 
6571         value: false, 
6572         validator: Lang.isBoolean, 
6573         suppressEvent: true, 
6574         supercedes: [_TEXT]
6575     }, 
6576
6577     STRONG_EMPHASIS_CONFIG = { 
6578         key: _STRONG_EMPHASIS, 
6579         value: false, 
6580         validator: Lang.isBoolean, 
6581         suppressEvent: true,
6582         supercedes: [_TEXT]
6583     },
6584
6585     CHECKED_CONFIG = { 
6586         key: _CHECKED, 
6587         value: false, 
6588         validator: Lang.isBoolean, 
6589         suppressEvent: true, 
6590         supercedes: [_DISABLED, _SELECTED]
6591     }, 
6592
6593     SUBMENU_CONFIG = { 
6594         key: _SUBMENU,
6595         suppressEvent: true,
6596         supercedes: [_DISABLED, _SELECTED]
6597     },
6598
6599     DISABLED_CONFIG = { 
6600         key: _DISABLED, 
6601         value: false, 
6602         validator: Lang.isBoolean, 
6603         suppressEvent: true,
6604         supercedes: [_TEXT, _SELECTED]
6605     },
6606
6607     SELECTED_CONFIG = { 
6608         key: _SELECTED, 
6609         value: false, 
6610         validator: Lang.isBoolean, 
6611         suppressEvent: true
6612     },
6613
6614     ONCLICK_CONFIG = { 
6615         key: _ONCLICK,
6616         suppressEvent: true
6617     },
6618
6619     CLASS_NAME_CONFIG = { 
6620         key: _CLASSNAME, 
6621         value: null, 
6622         validator: Lang.isString,
6623         suppressEvent: true
6624     },
6625     
6626     KEY_LISTENER_CONFIG = {
6627         key: "keylistener", 
6628         value: null, 
6629         suppressEvent: true
6630     },
6631
6632     m_oMenuItemTemplate = null,
6633
6634     CLASS_NAMES = {};
6635
6636
6637 /**
6638 * @method getClassNameForState
6639 * @description Returns a class name for the specified prefix and state.  If the class name does not 
6640 * yet exist, it is created and stored in the CLASS_NAMES object to increase performance.
6641 * @private
6642 * @param {String} prefix String representing the prefix for the class name
6643 * @param {String} state String representing a state - "disabled," "checked," etc.
6644 */  
6645 var getClassNameForState = function (prefix, state) {
6646
6647     var oClassNames = CLASS_NAMES[prefix];
6648     
6649     if (!oClassNames) {
6650         CLASS_NAMES[prefix] = {};
6651         oClassNames = CLASS_NAMES[prefix];
6652     }
6653
6654
6655     var sClassName = oClassNames[state];
6656
6657     if (!sClassName) {
6658         sClassName = prefix + _HYPHEN + state;
6659         oClassNames[state] = sClassName;
6660     }
6661
6662     return sClassName;
6663     
6664 };
6665
6666
6667 /**
6668 * @method addClassNameForState
6669 * @description Applies a class name to a MenuItem instance's &#60;LI&#62; and &#60;A&#62; elements
6670 * that represents a MenuItem's state - "disabled," "checked," etc.
6671 * @private
6672 * @param {String} state String representing a state - "disabled," "checked," etc.
6673 */  
6674 var addClassNameForState = function (state) {
6675
6676     Dom.addClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state));
6677     Dom.addClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state));
6678
6679 };
6680
6681 /**
6682 * @method removeClassNameForState
6683 * @description Removes a class name from a MenuItem instance's &#60;LI&#62; and &#60;A&#62; elements
6684 * that represents a MenuItem's state - "disabled," "checked," etc.
6685 * @private
6686 * @param {String} state String representing a state - "disabled," "checked," etc.
6687 */  
6688 var removeClassNameForState = function (state) {
6689
6690     Dom.removeClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state));
6691     Dom.removeClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state));
6692
6693 };
6694
6695
6696 MenuItem.prototype = {
6697
6698     /**
6699     * @property CSS_CLASS_NAME
6700     * @description String representing the CSS class(es) to be applied to the 
6701     * <code>&#60;li&#62;</code> element of the menu item.
6702     * @default "yuimenuitem"
6703     * @final
6704     * @type String
6705     */
6706     CSS_CLASS_NAME: "yuimenuitem",
6707
6708
6709     /**
6710     * @property CSS_LABEL_CLASS_NAME
6711     * @description String representing the CSS class(es) to be applied to the 
6712     * menu item's <code>&#60;a&#62;</code> element.
6713     * @default "yuimenuitemlabel"
6714     * @final
6715     * @type String
6716     */
6717     CSS_LABEL_CLASS_NAME: "yuimenuitemlabel",
6718
6719
6720     /**
6721     * @property SUBMENU_TYPE
6722     * @description Object representing the type of menu to instantiate and 
6723     * add when parsing the child nodes of the menu item's source HTML element.
6724     * @final
6725     * @type YAHOO.widget.Menu
6726     */
6727     SUBMENU_TYPE: null,
6728
6729
6730
6731     // Private member variables
6732     
6733
6734     /**
6735     * @property _oAnchor
6736     * @description Object reference to the menu item's 
6737     * <code>&#60;a&#62;</code> element.
6738     * @default null 
6739     * @private
6740     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6741     * one-html.html#ID-48250443">HTMLAnchorElement</a>
6742     */
6743     _oAnchor: null,
6744     
6745     
6746     /**
6747     * @property _oHelpTextEM
6748     * @description Object reference to the menu item's help text 
6749     * <code>&#60;em&#62;</code> element.
6750     * @default null
6751     * @private
6752     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6753     * one-html.html#ID-58190037">HTMLElement</a>
6754     */
6755     _oHelpTextEM: null,
6756     
6757     
6758     /**
6759     * @property _oSubmenu
6760     * @description Object reference to the menu item's submenu.
6761     * @default null
6762     * @private
6763     * @type YAHOO.widget.Menu
6764     */
6765     _oSubmenu: null,
6766
6767
6768     /** 
6769     * @property _oOnclickAttributeValue
6770     * @description Object reference to the menu item's current value for the 
6771     * "onclick" configuration attribute.
6772     * @default null
6773     * @private
6774     * @type Object
6775     */
6776     _oOnclickAttributeValue: null,
6777
6778
6779     /**
6780     * @property _sClassName
6781     * @description The current value of the "classname" configuration attribute.
6782     * @default null
6783     * @private
6784     * @type String
6785     */
6786     _sClassName: null,
6787
6788
6789
6790     // Public properties
6791
6792
6793     /**
6794     * @property constructor
6795     * @description Object reference to the menu item's constructor function.
6796     * @default YAHOO.widget.MenuItem
6797     * @type YAHOO.widget.MenuItem
6798     */
6799     constructor: MenuItem,
6800
6801
6802     /**
6803     * @property index
6804     * @description Number indicating the ordinal position of the menu item in 
6805     * its group.
6806     * @default null
6807     * @type Number
6808     */
6809     index: null,
6810
6811
6812     /**
6813     * @property groupIndex
6814     * @description Number indicating the index of the group to which the menu 
6815     * item belongs.
6816     * @default null
6817     * @type Number
6818     */
6819     groupIndex: null,
6820
6821
6822     /**
6823     * @property parent
6824     * @description Object reference to the menu item's parent menu.
6825     * @default null
6826     * @type YAHOO.widget.Menu
6827     */
6828     parent: null,
6829
6830
6831     /**
6832     * @property element
6833     * @description Object reference to the menu item's 
6834     * <code>&#60;li&#62;</code> element.
6835     * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level
6836     * -one-html.html#ID-74680021">HTMLLIElement</a>
6837     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6838     * one-html.html#ID-74680021">HTMLLIElement</a>
6839     */
6840     element: null,
6841
6842
6843     /**
6844     * @property srcElement
6845     * @description Object reference to the HTML element (either 
6846     * <code>&#60;li&#62;</code>, <code>&#60;optgroup&#62;</code> or 
6847     * <code>&#60;option&#62;</code>) used create the menu item.
6848     * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
6849     * level-one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.
6850     * w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247"
6851     * >HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
6852     * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
6853     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6854     * one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.w3.
6855     * org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247">
6856     * HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
6857     * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
6858     */
6859     srcElement: null,
6860
6861
6862     /**
6863     * @property value
6864     * @description Object reference to the menu item's value.
6865     * @default null
6866     * @type Object
6867     */
6868     value: null,
6869
6870
6871     /**
6872     * @property browser
6873     * @deprecated Use YAHOO.env.ua
6874     * @description String representing the browser.
6875     * @type String
6876     */
6877     browser: Module.prototype.browser,
6878
6879
6880     /**
6881     * @property id
6882     * @description Id of the menu item's root <code>&#60;li&#62;</code> 
6883     * element.  This property should be set via the constructor using the 
6884     * configuration object literal.  If an id is not specified, then one will 
6885     * be created using the "generateId" method of the Dom utility.
6886     * @default null
6887     * @type String
6888     */
6889     id: null,
6890
6891
6892
6893     // Events
6894
6895
6896     /**
6897     * @event destroyEvent
6898     * @description Fires when the menu item's <code>&#60;li&#62;</code> 
6899     * element is removed from its parent <code>&#60;ul&#62;</code> element.
6900     * @type YAHOO.util.CustomEvent
6901     */
6902
6903
6904     /**
6905     * @event mouseOverEvent
6906     * @description Fires when the mouse has entered the menu item.  Passes 
6907     * back the DOM Event object as an argument.
6908     * @type YAHOO.util.CustomEvent
6909     */
6910
6911
6912     /**
6913     * @event mouseOutEvent
6914     * @description Fires when the mouse has left the menu item.  Passes back 
6915     * the DOM Event object as an argument.
6916     * @type YAHOO.util.CustomEvent
6917     */
6918
6919
6920     /**
6921     * @event mouseDownEvent
6922     * @description Fires when the user mouses down on the menu item.  Passes 
6923     * back the DOM Event object as an argument.
6924     * @type YAHOO.util.CustomEvent
6925     */
6926
6927
6928     /**
6929     * @event mouseUpEvent
6930     * @description Fires when the user releases a mouse button while the mouse 
6931     * is over the menu item.  Passes back the DOM Event object as an argument.
6932     * @type YAHOO.util.CustomEvent
6933     */
6934
6935
6936     /**
6937     * @event clickEvent
6938     * @description Fires when the user clicks the on the menu item.  Passes 
6939     * back the DOM Event object as an argument.
6940     * @type YAHOO.util.CustomEvent
6941     */
6942
6943
6944     /**
6945     * @event keyPressEvent
6946     * @description Fires when the user presses an alphanumeric key when the 
6947     * menu item has focus.  Passes back the DOM Event object as an argument.
6948     * @type YAHOO.util.CustomEvent
6949     */
6950
6951
6952     /**
6953     * @event keyDownEvent
6954     * @description Fires when the user presses a key when the menu item has 
6955     * focus.  Passes back the DOM Event object as an argument.
6956     * @type YAHOO.util.CustomEvent
6957     */
6958
6959
6960     /**
6961     * @event keyUpEvent
6962     * @description Fires when the user releases a key when the menu item has 
6963     * focus.  Passes back the DOM Event object as an argument.
6964     * @type YAHOO.util.CustomEvent
6965     */
6966
6967
6968     /**
6969     * @event focusEvent
6970     * @description Fires when the menu item receives focus.
6971     * @type YAHOO.util.CustomEvent
6972     */
6973
6974
6975     /**
6976     * @event blurEvent
6977     * @description Fires when the menu item loses the input focus.
6978     * @type YAHOO.util.CustomEvent
6979     */
6980
6981
6982     /**
6983     * @method init
6984     * @description The MenuItem class's initialization method. This method is 
6985     * automatically called by the constructor, and sets up all DOM references 
6986     * for pre-existing markup, and creates required markup if it is not 
6987     * already present.
6988     * @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6989     * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6990     * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 
6991     * the <code>&#60;li&#62;</code> element of the menu item.
6992     * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6993     * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
6994     * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
6995     * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6996     * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 
6997     * specifying the <code>&#60;option&#62;</code> element of the menu item.
6998     * @param {Object} p_oConfig Optional. Object literal specifying the 
6999     * configuration for the menu item. See configuration class documentation 
7000     * for more details.
7001     */
7002     init: function (p_oObject, p_oConfig) {
7003
7004
7005         if (!this.SUBMENU_TYPE) {
7006     
7007             this.SUBMENU_TYPE = Menu;
7008     
7009         }
7010
7011
7012         // Create the config object
7013
7014         this.cfg = new YAHOO.util.Config(this);
7015
7016         this.initDefaultConfig();
7017
7018         var oConfig = this.cfg,
7019             sURL = _HASH,
7020             oCustomEvent,
7021             aEventData,
7022             oAnchor,
7023             sTarget,
7024             sText,
7025             sId,
7026             i;
7027
7028
7029         if (Lang.isString(p_oObject)) {
7030
7031             this._createRootNodeStructure();
7032
7033             oConfig.queueProperty(_TEXT, p_oObject);
7034
7035         }
7036         else if (p_oObject && p_oObject.tagName) {
7037
7038             switch(p_oObject.tagName.toUpperCase()) {
7039
7040                 case _OPTION:
7041
7042                     this._createRootNodeStructure();
7043
7044                     oConfig.queueProperty(_TEXT, p_oObject.text);
7045                     oConfig.queueProperty(_DISABLED, p_oObject.disabled);
7046
7047                     this.value = p_oObject.value;
7048
7049                     this.srcElement = p_oObject;
7050
7051                 break;
7052
7053                 case _OPTGROUP:
7054
7055                     this._createRootNodeStructure();
7056
7057                     oConfig.queueProperty(_TEXT, p_oObject.label);
7058                     oConfig.queueProperty(_DISABLED, p_oObject.disabled);
7059
7060                     this.srcElement = p_oObject;
7061
7062                     this._initSubTree();
7063
7064                 break;
7065
7066                 case _LI_UPPERCASE:
7067
7068                     // Get the anchor node (if it exists)
7069                     
7070                     oAnchor = Dom.getFirstChild(p_oObject);
7071
7072
7073                     // Capture the "text" and/or the "URL"
7074
7075                     if (oAnchor) {
7076
7077                         sURL = oAnchor.getAttribute(_HREF, 2);
7078                         sTarget = oAnchor.getAttribute(_TARGET);
7079
7080                         sText = oAnchor.innerHTML;
7081
7082                     }
7083
7084                     this.srcElement = p_oObject;
7085                     this.element = p_oObject;
7086                     this._oAnchor = oAnchor;
7087
7088                     /*
7089                         Set these properties silently to sync up the 
7090                         configuration object without making changes to the 
7091                         element's DOM
7092                     */ 
7093
7094                     oConfig.setProperty(_TEXT, sText, true);
7095                     oConfig.setProperty(_URL, sURL, true);
7096                     oConfig.setProperty(_TARGET, sTarget, true);
7097
7098                     this._initSubTree();
7099
7100                 break;
7101
7102             }            
7103
7104         }
7105
7106
7107         if (this.element) {
7108
7109             sId = (this.srcElement || this.element).id;
7110
7111             if (!sId) {
7112
7113                 sId = this.id || Dom.generateId();
7114
7115                 this.element.id = sId;
7116
7117             }
7118
7119             this.id = sId;
7120
7121
7122             Dom.addClass(this.element, this.CSS_CLASS_NAME);
7123             Dom.addClass(this._oAnchor, this.CSS_LABEL_CLASS_NAME);
7124
7125
7126             i = EVENT_TYPES.length - 1;
7127
7128             do {
7129
7130                 aEventData = EVENT_TYPES[i];
7131
7132                 oCustomEvent = this.createEvent(aEventData[1]);
7133                 oCustomEvent.signature = CustomEvent.LIST;
7134                 
7135                 this[aEventData[0]] = oCustomEvent;
7136
7137             }
7138             while (i--);
7139
7140
7141             if (p_oConfig) {
7142     
7143                 oConfig.applyConfig(p_oConfig);
7144     
7145             }        
7146
7147             oConfig.fireQueue();
7148
7149         }
7150
7151     },
7152
7153
7154
7155     // Private methods
7156
7157     /**
7158     * @method _createRootNodeStructure
7159     * @description Creates the core DOM structure for the menu item.
7160     * @private
7161     */
7162     _createRootNodeStructure: function () {
7163
7164         var oElement,
7165             oAnchor;
7166
7167         if (!m_oMenuItemTemplate) {
7168
7169             m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE);
7170             m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE;
7171
7172         }
7173
7174         oElement = m_oMenuItemTemplate.cloneNode(true);
7175         oElement.className = this.CSS_CLASS_NAME;
7176
7177         oAnchor = oElement.firstChild;
7178         oAnchor.className = this.CSS_LABEL_CLASS_NAME;
7179
7180         this.element = oElement;
7181         this._oAnchor = oAnchor;
7182
7183     },
7184
7185
7186     /**
7187     * @method _initSubTree
7188     * @description Iterates the source element's childNodes collection and uses 
7189     * the child nodes to instantiate other menus.
7190     * @private
7191     */
7192     _initSubTree: function () {
7193
7194         var oSrcEl = this.srcElement,
7195             oConfig = this.cfg,
7196             oNode,
7197             aOptions,
7198             nOptions,
7199             oMenu,
7200             n;
7201
7202
7203         if (oSrcEl.childNodes.length > 0) {
7204
7205             if (this.parent.lazyLoad && this.parent.srcElement && 
7206                 this.parent.srcElement.tagName.toUpperCase() == _SELECT) {
7207
7208                 oConfig.setProperty(
7209                         _SUBMENU, 
7210                         { id: Dom.generateId(), itemdata: oSrcEl.childNodes }
7211                     );
7212
7213             }
7214             else {
7215
7216                 oNode = oSrcEl.firstChild;
7217                 aOptions = [];
7218     
7219                 do {
7220     
7221                     if (oNode && oNode.tagName) {
7222     
7223                         switch(oNode.tagName.toUpperCase()) {
7224                 
7225                             case _DIV:
7226                 
7227                                 oConfig.setProperty(_SUBMENU, oNode);
7228                 
7229                             break;
7230          
7231                             case _OPTION:
7232         
7233                                 aOptions[aOptions.length] = oNode;
7234         
7235                             break;
7236                
7237                         }
7238                     
7239                     }
7240                 
7241                 }        
7242                 while((oNode = oNode.nextSibling));
7243     
7244     
7245                 nOptions = aOptions.length;
7246     
7247                 if (nOptions > 0) {
7248     
7249                     oMenu = new this.SUBMENU_TYPE(Dom.generateId());
7250                     
7251                     oConfig.setProperty(_SUBMENU, oMenu);
7252     
7253                     for(n=0; n<nOptions; n++) {
7254         
7255                         oMenu.addItem((new oMenu.ITEM_TYPE(aOptions[n])));
7256         
7257                     }
7258         
7259                 }
7260             
7261             }
7262
7263         }
7264
7265     },
7266
7267
7268
7269     // Event handlers for configuration properties
7270
7271
7272     /**
7273     * @method configText
7274     * @description Event handler for when the "text" configuration property of 
7275     * the menu item changes.
7276     * @param {String} p_sType String representing the name of the event that 
7277     * was fired.
7278     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7279     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7280     * that fired the event.
7281     */
7282     configText: function (p_sType, p_aArgs, p_oItem) {
7283
7284         var sText = p_aArgs[0],
7285             oConfig = this.cfg,
7286             oAnchor = this._oAnchor,
7287             sHelpText = oConfig.getProperty(_HELP_TEXT),
7288             sHelpTextHTML = _EMPTY_STRING,
7289             sEmphasisStartTag = _EMPTY_STRING,
7290             sEmphasisEndTag = _EMPTY_STRING;
7291
7292
7293         if (sText) {
7294
7295
7296             if (sHelpText) {
7297                     
7298                 sHelpTextHTML = _START_HELP_TEXT + sHelpText + _END_EM;
7299             
7300             }
7301
7302
7303             if (oConfig.getProperty(_EMPHASIS)) {
7304
7305                 sEmphasisStartTag = _START_EM;
7306                 sEmphasisEndTag = _END_EM;
7307
7308             }
7309
7310
7311             if (oConfig.getProperty(_STRONG_EMPHASIS)) {
7312
7313                 sEmphasisStartTag = _START_STRONG;
7314                 sEmphasisEndTag = _END_STRONG;
7315             
7316             }
7317
7318
7319             oAnchor.innerHTML = (sEmphasisStartTag + sText + sEmphasisEndTag + sHelpTextHTML);
7320
7321         }
7322
7323     },
7324
7325
7326     /**
7327     * @method configHelpText
7328     * @description Event handler for when the "helptext" configuration property 
7329     * of the menu item changes.
7330     * @param {String} p_sType String representing the name of the event that 
7331     * was fired.
7332     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7333     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7334     * that fired the event.
7335     */    
7336     configHelpText: function (p_sType, p_aArgs, p_oItem) {
7337
7338         this.cfg.refireEvent(_TEXT);
7339
7340     },
7341
7342
7343     /**
7344     * @method configURL
7345     * @description Event handler for when the "url" configuration property of 
7346     * the menu item changes.
7347     * @param {String} p_sType String representing the name of the event that 
7348     * was fired.
7349     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7350     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7351     * that fired the event.
7352     */    
7353     configURL: function (p_sType, p_aArgs, p_oItem) {
7354
7355         var sURL = p_aArgs[0];
7356
7357         if (!sURL) {
7358
7359             sURL = _HASH;
7360
7361         }
7362
7363         var oAnchor = this._oAnchor;
7364
7365         if (UA.opera) {
7366
7367             oAnchor.removeAttribute(_HREF);
7368         
7369         }
7370
7371         oAnchor.setAttribute(_HREF, sURL);
7372
7373     },
7374
7375
7376     /**
7377     * @method configTarget
7378     * @description Event handler for when the "target" configuration property 
7379     * of the menu item changes.  
7380     * @param {String} p_sType String representing the name of the event that 
7381     * was fired.
7382     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7383     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7384     * that fired the event.
7385     */    
7386     configTarget: function (p_sType, p_aArgs, p_oItem) {
7387
7388         var sTarget = p_aArgs[0],
7389             oAnchor = this._oAnchor;
7390
7391         if (sTarget && sTarget.length > 0) {
7392
7393             oAnchor.setAttribute(_TARGET, sTarget);
7394
7395         }
7396         else {
7397
7398             oAnchor.removeAttribute(_TARGET);
7399         
7400         }
7401
7402     },
7403
7404
7405     /**
7406     * @method configEmphasis
7407     * @description Event handler for when the "emphasis" configuration property
7408     * of the menu item changes.
7409     * @param {String} p_sType String representing the name of the event that 
7410     * was fired.
7411     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7412     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7413     * that fired the event.
7414     */    
7415     configEmphasis: function (p_sType, p_aArgs, p_oItem) {
7416
7417         var bEmphasis = p_aArgs[0],
7418             oConfig = this.cfg;
7419
7420
7421         if (bEmphasis && oConfig.getProperty(_STRONG_EMPHASIS)) {
7422
7423             oConfig.setProperty(_STRONG_EMPHASIS, false);
7424
7425         }
7426
7427
7428         oConfig.refireEvent(_TEXT);
7429
7430     },
7431
7432
7433     /**
7434     * @method configStrongEmphasis
7435     * @description Event handler for when the "strongemphasis" configuration 
7436     * property of the menu item changes.
7437     * @param {String} p_sType String representing the name of the event that 
7438     * was fired.
7439     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7440     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7441     * that fired the event.
7442     */    
7443     configStrongEmphasis: function (p_sType, p_aArgs, p_oItem) {
7444
7445         var bStrongEmphasis = p_aArgs[0],
7446             oConfig = this.cfg;
7447
7448
7449         if (bStrongEmphasis && oConfig.getProperty(_EMPHASIS)) {
7450
7451             oConfig.setProperty(_EMPHASIS, false);
7452
7453         }
7454
7455         oConfig.refireEvent(_TEXT);
7456
7457     },
7458
7459
7460     /**
7461     * @method configChecked
7462     * @description Event handler for when the "checked" configuration property 
7463     * of the menu item changes. 
7464     * @param {String} p_sType String representing the name of the event that 
7465     * was fired.
7466     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7467     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7468     * that fired the event.
7469     */    
7470     configChecked: function (p_sType, p_aArgs, p_oItem) {
7471
7472         var bChecked = p_aArgs[0],
7473             oConfig = this.cfg;
7474
7475
7476         if (bChecked) {
7477
7478             addClassNameForState.call(this, _CHECKED);
7479
7480         }
7481         else {
7482
7483             removeClassNameForState.call(this, _CHECKED);
7484         }
7485
7486
7487         oConfig.refireEvent(_TEXT);
7488
7489
7490         if (oConfig.getProperty(_DISABLED)) {
7491
7492             oConfig.refireEvent(_DISABLED);
7493
7494         }
7495
7496
7497         if (oConfig.getProperty(_SELECTED)) {
7498
7499             oConfig.refireEvent(_SELECTED);
7500
7501         }
7502
7503     },
7504
7505
7506
7507     /**
7508     * @method configDisabled
7509     * @description Event handler for when the "disabled" configuration property 
7510     * of the menu item changes. 
7511     * @param {String} p_sType String representing the name of the event that 
7512     * was fired.
7513     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7514     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7515     * that fired the event.
7516     */    
7517     configDisabled: function (p_sType, p_aArgs, p_oItem) {
7518
7519         var bDisabled = p_aArgs[0],
7520             oConfig = this.cfg,
7521             oSubmenu = oConfig.getProperty(_SUBMENU),
7522             bChecked = oConfig.getProperty(_CHECKED);
7523
7524
7525         if (bDisabled) {
7526
7527             if (oConfig.getProperty(_SELECTED)) {
7528
7529                 oConfig.setProperty(_SELECTED, false);
7530
7531             }
7532
7533
7534             addClassNameForState.call(this, _DISABLED);
7535
7536
7537             if (oSubmenu) {
7538
7539                 addClassNameForState.call(this, _HAS_SUBMENU_DISABLED);
7540             
7541             }
7542             
7543
7544             if (bChecked) {
7545
7546                 addClassNameForState.call(this, _CHECKED_DISABLED);
7547
7548             }
7549
7550         }
7551         else {
7552
7553             removeClassNameForState.call(this, _DISABLED);
7554
7555
7556             if (oSubmenu) {
7557
7558                 removeClassNameForState.call(this, _HAS_SUBMENU_DISABLED);
7559             
7560             }
7561             
7562
7563             if (bChecked) {
7564
7565                 removeClassNameForState.call(this, _CHECKED_DISABLED);
7566
7567             }
7568
7569         }
7570
7571     },
7572
7573
7574     /**
7575     * @method configSelected
7576     * @description Event handler for when the "selected" configuration property 
7577     * of the menu item changes. 
7578     * @param {String} p_sType String representing the name of the event that 
7579     * was fired.
7580     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7581     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7582     * that fired the event.
7583     */    
7584     configSelected: function (p_sType, p_aArgs, p_oItem) {
7585
7586         var oConfig = this.cfg,
7587             oAnchor = this._oAnchor,
7588             
7589             bSelected = p_aArgs[0],
7590             bChecked = oConfig.getProperty(_CHECKED),
7591             oSubmenu = oConfig.getProperty(_SUBMENU);
7592
7593
7594         if (UA.opera) {
7595
7596             oAnchor.blur();
7597         
7598         }
7599
7600
7601         if (bSelected && !oConfig.getProperty(_DISABLED)) {
7602
7603             addClassNameForState.call(this, _SELECTED);
7604
7605
7606             if (oSubmenu) {
7607
7608                 addClassNameForState.call(this, _HAS_SUBMENU_SELECTED);
7609             
7610             }
7611
7612
7613             if (bChecked) {
7614
7615                 addClassNameForState.call(this, _CHECKED_SELECTED);
7616
7617             }
7618
7619         }
7620         else {
7621
7622             removeClassNameForState.call(this, _SELECTED);
7623
7624
7625             if (oSubmenu) {
7626
7627                 removeClassNameForState.call(this, _HAS_SUBMENU_SELECTED);
7628             
7629             }
7630
7631
7632             if (bChecked) {
7633
7634                 removeClassNameForState.call(this, _CHECKED_SELECTED);
7635
7636             }
7637
7638         }
7639
7640
7641         if (this.hasFocus() && UA.opera) {
7642         
7643             oAnchor.focus();
7644         
7645         }
7646
7647     },
7648
7649
7650     /**
7651     * @method _onSubmenuBeforeHide
7652     * @description "beforehide" Custom Event handler for a submenu.
7653     * @private
7654     * @param {String} p_sType String representing the name of the event that 
7655     * was fired.
7656     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7657     */
7658     _onSubmenuBeforeHide: function (p_sType, p_aArgs) {
7659
7660         var oItem = this.parent,
7661             oMenu;
7662
7663         function onHide() {
7664
7665             oItem._oAnchor.blur();
7666             oMenu.beforeHideEvent.unsubscribe(onHide);
7667         
7668         }
7669
7670
7671         if (oItem.hasFocus()) {
7672
7673             oMenu = oItem.parent;
7674
7675             oMenu.beforeHideEvent.subscribe(onHide);
7676         
7677         }
7678     
7679     },
7680
7681
7682     /**
7683     * @method configSubmenu
7684     * @description Event handler for when the "submenu" configuration property 
7685     * of the menu item changes. 
7686     * @param {String} p_sType String representing the name of the event that 
7687     * was fired.
7688     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7689     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7690     * that fired the event.
7691     */
7692     configSubmenu: function (p_sType, p_aArgs, p_oItem) {
7693
7694         var oSubmenu = p_aArgs[0],
7695             oConfig = this.cfg,
7696             bLazyLoad = this.parent && this.parent.lazyLoad,
7697             oMenu,
7698             sSubmenuId,
7699             oSubmenuConfig;
7700
7701
7702         if (oSubmenu) {
7703
7704             if (oSubmenu instanceof Menu) {
7705
7706                 oMenu = oSubmenu;
7707                 oMenu.parent = this;
7708                 oMenu.lazyLoad = bLazyLoad;
7709
7710             }
7711             else if (Lang.isObject(oSubmenu) && oSubmenu.id && !oSubmenu.nodeType) {
7712
7713                 sSubmenuId = oSubmenu.id;
7714                 oSubmenuConfig = oSubmenu;
7715
7716                 oSubmenuConfig.lazyload = bLazyLoad;
7717                 oSubmenuConfig.parent = this;
7718
7719                 oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig);
7720
7721
7722                 // Set the value of the property to the Menu instance
7723
7724                 oConfig.setProperty(_SUBMENU, oMenu, true);
7725
7726             }
7727             else {
7728
7729                 oMenu = new this.SUBMENU_TYPE(oSubmenu, { lazyload: bLazyLoad, parent: this });
7730
7731
7732                 // Set the value of the property to the Menu instance
7733                 
7734                 oConfig.setProperty(_SUBMENU, oMenu, true);
7735
7736             }
7737
7738
7739             if (oMenu) {
7740
7741                 oMenu.cfg.setProperty(_PREVENT_CONTEXT_OVERLAP, true);
7742
7743                 addClassNameForState.call(this, _HAS_SUBMENU);
7744
7745
7746                 if (oConfig.getProperty(_URL) === _HASH) {
7747                 
7748                     oConfig.setProperty(_URL, (_HASH + oMenu.id));
7749                 
7750                 }
7751
7752
7753                 this._oSubmenu = oMenu;
7754
7755
7756                 if (UA.opera) {
7757                 
7758                     oMenu.beforeHideEvent.subscribe(this._onSubmenuBeforeHide);               
7759                 
7760                 }
7761             
7762             }
7763
7764         }
7765         else {
7766
7767             removeClassNameForState.call(this, _HAS_SUBMENU);
7768
7769             if (this._oSubmenu) {
7770
7771                 this._oSubmenu.destroy();
7772
7773             }
7774
7775         }
7776
7777
7778         if (oConfig.getProperty(_DISABLED)) {
7779
7780             oConfig.refireEvent(_DISABLED);
7781
7782         }
7783
7784
7785         if (oConfig.getProperty(_SELECTED)) {
7786
7787             oConfig.refireEvent(_SELECTED);
7788
7789         }
7790
7791     },
7792
7793
7794     /**
7795     * @method configOnClick
7796     * @description Event handler for when the "onclick" configuration property 
7797     * of the menu item changes. 
7798     * @param {String} p_sType String representing the name of the event that 
7799     * was fired.
7800     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7801     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7802     * that fired the event.
7803     */
7804     configOnClick: function (p_sType, p_aArgs, p_oItem) {
7805
7806         var oObject = p_aArgs[0];
7807
7808         /*
7809             Remove any existing listeners if a "click" event handler has 
7810             already been specified.
7811         */
7812
7813         if (this._oOnclickAttributeValue && (this._oOnclickAttributeValue != oObject)) {
7814
7815             this.clickEvent.unsubscribe(this._oOnclickAttributeValue.fn, 
7816                                 this._oOnclickAttributeValue.obj);
7817
7818             this._oOnclickAttributeValue = null;
7819
7820         }
7821
7822
7823         if (!this._oOnclickAttributeValue && Lang.isObject(oObject) && 
7824             Lang.isFunction(oObject.fn)) {
7825             
7826             this.clickEvent.subscribe(oObject.fn, 
7827                 ((_OBJ in oObject) ? oObject.obj : this), 
7828                 ((_SCOPE in oObject) ? oObject.scope : null) );
7829
7830             this._oOnclickAttributeValue = oObject;
7831
7832         }
7833     
7834     },
7835
7836
7837     /**
7838     * @method configClassName
7839     * @description Event handler for when the "classname" configuration 
7840     * property of a menu item changes.
7841     * @param {String} p_sType String representing the name of the event that 
7842     * was fired.
7843     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7844     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7845     * that fired the event.
7846     */
7847     configClassName: function (p_sType, p_aArgs, p_oItem) {
7848     
7849         var sClassName = p_aArgs[0];
7850     
7851         if (this._sClassName) {
7852     
7853             Dom.removeClass(this.element, this._sClassName);
7854     
7855         }
7856     
7857         Dom.addClass(this.element, sClassName);
7858         this._sClassName = sClassName;
7859     
7860     },
7861
7862
7863     /**
7864     * @method _dispatchClickEvent
7865     * @description Dispatches a DOM "click" event to the anchor element of a 
7866     * MenuItem instance.
7867     * @private  
7868     */
7869     _dispatchClickEvent: function () {
7870
7871         var oMenuItem = this,
7872             oAnchor;
7873
7874         if (!oMenuItem.cfg.getProperty(_DISABLED)) {
7875             oAnchor = Dom.getFirstChild(oMenuItem.element);
7876
7877             //  Dispatch a "click" event to the MenuItem's anchor so that its
7878             //  "click" event handlers will get called in response to the user 
7879             //  pressing the keyboard shortcut defined by the "keylistener"
7880             //  configuration property.
7881
7882             this._dispatchDOMClick(oAnchor);
7883         }
7884     },
7885
7886     /**
7887      * Utility method to dispatch a DOM click event on the HTMLElement passed in
7888      *
7889      * @method _dispatchDOMClick
7890      * @protected
7891      * @param {HTMLElement} el
7892      */    
7893     _dispatchDOMClick : function(el) {
7894         var oEvent;
7895
7896         // Choose the standards path for IE9
7897         if (UA.ie && UA.ie < 9) {
7898             el.fireEvent(_ONCLICK);
7899         } else {
7900             if ((UA.gecko && UA.gecko >= 1.9) || UA.opera || UA.webkit) {
7901                 oEvent = document.createEvent("HTMLEvents");
7902                 oEvent.initEvent(_CLICK, true, true);
7903             } else {
7904                 oEvent = document.createEvent("MouseEvents");
7905                 oEvent.initMouseEvent(_CLICK, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
7906             }
7907             el.dispatchEvent(oEvent);
7908         }
7909     },
7910
7911     /**
7912     * @method _createKeyListener
7913     * @description "show" event handler for a Menu instance - responsible for 
7914     * setting up the KeyListener instance for a MenuItem.
7915     * @private  
7916     * @param {String} type String representing the name of the event that 
7917     * was fired.
7918     * @param {Array} args Array of arguments sent when the event was fired.
7919     * @param {Array} keyData Array of arguments sent when the event was fired.
7920     */
7921     _createKeyListener: function (type, args, keyData) {
7922
7923         var oMenuItem = this,
7924             oMenu = oMenuItem.parent;
7925
7926         var oKeyListener = new YAHOO.util.KeyListener(
7927                                         oMenu.element.ownerDocument, 
7928                                         keyData, 
7929                                         {
7930                                             fn: oMenuItem._dispatchClickEvent, 
7931                                             scope: oMenuItem, 
7932                                             correctScope: true });
7933
7934
7935         if (oMenu.cfg.getProperty(_VISIBLE)) {
7936             oKeyListener.enable();
7937         }
7938
7939
7940         oMenu.subscribe(_SHOW, oKeyListener.enable, null, oKeyListener);
7941         oMenu.subscribe(_HIDE, oKeyListener.disable, null, oKeyListener);
7942         
7943         oMenuItem._keyListener = oKeyListener;
7944         
7945         oMenu.unsubscribe(_SHOW, oMenuItem._createKeyListener, keyData);
7946         
7947     },
7948
7949
7950     /**
7951     * @method configKeyListener
7952     * @description Event handler for when the "keylistener" configuration 
7953     * property of a menu item changes.
7954     * @param {String} p_sType String representing the name of the event that 
7955     * was fired.
7956     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7957     */
7958     configKeyListener: function (p_sType, p_aArgs) {
7959
7960         var oKeyData = p_aArgs[0],
7961             oMenuItem = this,
7962             oMenu = oMenuItem.parent;
7963
7964         if (oMenuItem._keyData) {
7965
7966             //  Unsubscribe from the "show" event in case the keylistener 
7967             //  config was changed before the Menu was ever made visible.
7968
7969             oMenu.unsubscribe(_SHOW, 
7970                     oMenuItem._createKeyListener, oMenuItem._keyData);
7971
7972             oMenuItem._keyData = null;                                  
7973                     
7974         }
7975
7976
7977         //      Tear down for the previous value of the "keylistener" property
7978
7979         if (oMenuItem._keyListener) {
7980
7981             oMenu.unsubscribe(_SHOW, oMenuItem._keyListener.enable);
7982             oMenu.unsubscribe(_HIDE, oMenuItem._keyListener.disable);
7983
7984             oMenuItem._keyListener.disable();
7985             oMenuItem._keyListener = null;
7986
7987         }
7988
7989
7990         if (oKeyData) {
7991     
7992             oMenuItem._keyData = oKeyData;
7993
7994             //  Defer the creation of the KeyListener instance until the 
7995             //  parent Menu is visible.  This is necessary since the 
7996             //  KeyListener instance needs to be bound to the document the 
7997             //  Menu has been rendered into.  Deferring creation of the 
7998             //  KeyListener instance also improves performance.
7999
8000             oMenu.subscribe(_SHOW, oMenuItem._createKeyListener, 
8001                 oKeyData, oMenuItem);
8002         }
8003     
8004     },
8005
8006
8007     // Public methods
8008
8009
8010     /**
8011     * @method initDefaultConfig
8012     * @description Initializes an item's configurable properties.
8013     */
8014     initDefaultConfig : function () {
8015
8016         var oConfig = this.cfg;
8017
8018
8019         // Define the configuration attributes
8020
8021         /**
8022         * @config text
8023         * @description String or markup specifying the text label for the menu item.  
8024         * When building a menu from existing HTML the value of this property
8025         * will be interpreted from the menu's markup. The text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
8026         * @default ""
8027         * @type HTML
8028         */
8029         oConfig.addProperty(
8030             TEXT_CONFIG.key, 
8031             { 
8032                 handler: this.configText, 
8033                 value: TEXT_CONFIG.value, 
8034                 validator: TEXT_CONFIG.validator, 
8035                 suppressEvent: TEXT_CONFIG.suppressEvent 
8036             }
8037         );
8038         
8039
8040         /**
8041         * @config helptext
8042         * @description String or markup specifying additional instructional text to 
8043         * accompany the text for the menu item. The helptext is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
8044         * @deprecated Use "text" configuration property to add help text markup.  
8045         * For example: <code>oMenuItem.cfg.setProperty("text", "Copy &#60;em 
8046         * class=\"helptext\"&#62;Ctrl + C&#60;/em&#62;");</code>
8047         * @default null
8048         * @type HTML|<a href="http://www.w3.org/TR/
8049         * 2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
8050         * HTMLElement</a>
8051         */
8052         oConfig.addProperty(
8053             HELP_TEXT_CONFIG.key,
8054             {
8055                 handler: this.configHelpText, 
8056                 supercedes: HELP_TEXT_CONFIG.supercedes,
8057                 suppressEvent: HELP_TEXT_CONFIG.suppressEvent 
8058             }
8059         );
8060
8061
8062         /**
8063         * @config url
8064         * @description String specifying the URL for the menu item's anchor's 
8065         * "href" attribute.  When building a menu from existing HTML the value 
8066         * of this property will be interpreted from the menu's markup. Markup for the menu item content. The url is inserted into the DOM as an attribute value, and should be escaped by the implementor if coming from an external source.
8067         * @default "#"
8068         * @type String
8069         */        
8070         oConfig.addProperty(
8071             URL_CONFIG.key, 
8072             {
8073                 handler: this.configURL, 
8074                 value: URL_CONFIG.value, 
8075                 suppressEvent: URL_CONFIG.suppressEvent
8076             }
8077         );
8078
8079
8080         /**
8081         * @config target
8082         * @description String specifying the value for the "target" attribute 
8083         * of the menu item's anchor element. <strong>Specifying a target will 
8084         * require the user to click directly on the menu item's anchor node in
8085         * order to cause the browser to navigate to the specified URL.</strong> 
8086         * When building a menu from existing HTML the value of this property 
8087         * will be interpreted from the menu's markup. The target is inserted into the DOM as an attribute value, and should be escaped by the implementor if coming from an external source.
8088         * @default null
8089         * @type String
8090         */        
8091         oConfig.addProperty(
8092             TARGET_CONFIG.key, 
8093             {
8094                 handler: this.configTarget, 
8095                 suppressEvent: TARGET_CONFIG.suppressEvent
8096             }
8097         );
8098
8099
8100         /**
8101         * @config emphasis
8102         * @description Boolean indicating if the text of the menu item will be 
8103         * rendered with emphasis.
8104         * @deprecated Use the "text" configuration property to add emphasis.  
8105         * For example: <code>oMenuItem.cfg.setProperty("text", "&#60;em&#62;Some 
8106         * Text&#60;/em&#62;");</code>
8107         * @default false
8108         * @type Boolean
8109         */
8110         oConfig.addProperty(
8111             EMPHASIS_CONFIG.key, 
8112             { 
8113                 handler: this.configEmphasis, 
8114                 value: EMPHASIS_CONFIG.value, 
8115                 validator: EMPHASIS_CONFIG.validator, 
8116                 suppressEvent: EMPHASIS_CONFIG.suppressEvent,
8117                 supercedes: EMPHASIS_CONFIG.supercedes
8118             }
8119         );
8120
8121
8122         /**
8123         * @config strongemphasis
8124         * @description Boolean indicating if the text of the menu item will be 
8125         * rendered with strong emphasis.
8126         * @deprecated Use the "text" configuration property to add strong emphasis.  
8127         * For example: <code>oMenuItem.cfg.setProperty("text", "&#60;strong&#62; 
8128         * Some Text&#60;/strong&#62;");</code>
8129         * @default false
8130         * @type Boolean
8131         */
8132         oConfig.addProperty(
8133             STRONG_EMPHASIS_CONFIG.key,
8134             {
8135                 handler: this.configStrongEmphasis,
8136                 value: STRONG_EMPHASIS_CONFIG.value,
8137                 validator: STRONG_EMPHASIS_CONFIG.validator,
8138                 suppressEvent: STRONG_EMPHASIS_CONFIG.suppressEvent,
8139                 supercedes: STRONG_EMPHASIS_CONFIG.supercedes
8140             }
8141         );
8142
8143
8144         /**
8145         * @config checked
8146         * @description Boolean indicating if the menu item should be rendered 
8147         * with a checkmark.
8148         * @default false
8149         * @type Boolean
8150         */
8151         oConfig.addProperty(
8152             CHECKED_CONFIG.key, 
8153             {
8154                 handler: this.configChecked, 
8155                 value: CHECKED_CONFIG.value, 
8156                 validator: CHECKED_CONFIG.validator, 
8157                 suppressEvent: CHECKED_CONFIG.suppressEvent,
8158                 supercedes: CHECKED_CONFIG.supercedes
8159             } 
8160         );
8161
8162
8163         /**
8164         * @config disabled
8165         * @description Boolean indicating if the menu item should be disabled.  
8166         * (Disabled menu items are  dimmed and will not respond to user input 
8167         * or fire events.)
8168         * @default false
8169         * @type Boolean
8170         */
8171         oConfig.addProperty(
8172             DISABLED_CONFIG.key,
8173             {
8174                 handler: this.configDisabled,
8175                 value: DISABLED_CONFIG.value,
8176                 validator: DISABLED_CONFIG.validator,
8177                 suppressEvent: DISABLED_CONFIG.suppressEvent
8178             }
8179         );
8180
8181
8182         /**
8183         * @config selected
8184         * @description Boolean indicating if the menu item should 
8185         * be highlighted.
8186         * @default false
8187         * @type Boolean
8188         */
8189         oConfig.addProperty(
8190             SELECTED_CONFIG.key,
8191             {
8192                 handler: this.configSelected,
8193                 value: SELECTED_CONFIG.value,
8194                 validator: SELECTED_CONFIG.validator,
8195                 suppressEvent: SELECTED_CONFIG.suppressEvent
8196             }
8197         );
8198
8199
8200         /**
8201         * @config submenu
8202         * @description Object specifying the submenu to be appended to the 
8203         * menu item.  The value can be one of the following: <ul><li>Object 
8204         * specifying a Menu instance.</li><li>Object literal specifying the
8205         * menu to be created.  Format: <code>{ id: [menu id], itemdata: 
8206         * [<a href="YAHOO.widget.Menu.html#itemData">array of values for 
8207         * items</a>] }</code>.</li><li>String specifying the id attribute 
8208         * of the <code>&#60;div&#62;</code> element of the menu.</li><li>
8209         * Object specifying the <code>&#60;div&#62;</code> element of the 
8210         * menu.</li></ul>
8211         * @default null
8212         * @type Menu|String|Object|<a href="http://www.w3.org/TR/2000/
8213         * WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
8214         * HTMLElement</a>
8215         */
8216         oConfig.addProperty(
8217             SUBMENU_CONFIG.key, 
8218             {
8219                 handler: this.configSubmenu, 
8220                 supercedes: SUBMENU_CONFIG.supercedes,
8221                 suppressEvent: SUBMENU_CONFIG.suppressEvent
8222             }
8223         );
8224
8225
8226         /**
8227         * @config onclick
8228         * @description Object literal representing the code to be executed when 
8229         * the item is clicked.  Format:<br> <code> {<br> 
8230         * <strong>fn:</strong> Function,   &#47;&#47; The handler to call when 
8231         * the event fires.<br> <strong>obj:</strong> Object, &#47;&#47; An 
8232         * object to  pass back to the handler.<br> <strong>scope:</strong> 
8233         * Object &#47;&#47; The object to use for the scope of the handler.
8234         * <br> } </code>
8235         * @type Object
8236         * @default null
8237         */
8238         oConfig.addProperty(
8239             ONCLICK_CONFIG.key, 
8240             {
8241                 handler: this.configOnClick, 
8242                 suppressEvent: ONCLICK_CONFIG.suppressEvent 
8243             }
8244         );
8245
8246
8247         /**
8248         * @config classname
8249         * @description CSS class to be applied to the menu item's root 
8250         * <code>&#60;li&#62;</code> element.  The specified class(es) are 
8251         * appended in addition to the default class as specified by the menu 
8252         * item's CSS_CLASS_NAME constant.
8253         * @default null
8254         * @type String
8255         */
8256         oConfig.addProperty(
8257             CLASS_NAME_CONFIG.key, 
8258             { 
8259                 handler: this.configClassName,
8260                 value: CLASS_NAME_CONFIG.value, 
8261                 validator: CLASS_NAME_CONFIG.validator,
8262                 suppressEvent: CLASS_NAME_CONFIG.suppressEvent 
8263             }
8264         );
8265
8266
8267         /**
8268         * @config keylistener
8269         * @description Object literal representing the key(s) that can be used 
8270         * to trigger the MenuItem's "click" event.  Possible attributes are 
8271         * shift (boolean), alt (boolean), ctrl (boolean) and keys (either an int 
8272         * or an array of ints representing keycodes).
8273         * @default null
8274         * @type Object
8275         */
8276         oConfig.addProperty(
8277             KEY_LISTENER_CONFIG.key, 
8278             { 
8279                 handler: this.configKeyListener,
8280                 value: KEY_LISTENER_CONFIG.value, 
8281                 suppressEvent: KEY_LISTENER_CONFIG.suppressEvent 
8282             }
8283         );
8284
8285     },
8286
8287     /**
8288     * @method getNextSibling
8289     * @description Finds the menu item's next sibling.
8290     * @return YAHOO.widget.MenuItem
8291     */
8292     getNextSibling: function () {
8293     
8294         var isUL = function (el) {
8295                 return (el.nodeName.toLowerCase() === "ul");
8296             },
8297     
8298             menuitemEl = this.element,
8299             next = Dom.getNextSibling(menuitemEl),
8300             parent,
8301             sibling,
8302             list;
8303         
8304         if (!next) {
8305             
8306             parent = menuitemEl.parentNode;
8307             sibling = Dom.getNextSiblingBy(parent, isUL);
8308             
8309             if (sibling) {
8310                 list = sibling;
8311             }
8312             else {
8313                 list = Dom.getFirstChildBy(parent.parentNode, isUL);
8314             }
8315             
8316             next = Dom.getFirstChild(list);
8317             
8318         }
8319
8320         return YAHOO.widget.MenuManager.getMenuItem(next.id);
8321
8322     },
8323
8324     /**
8325     * @method getNextEnabledSibling
8326     * @description Finds the menu item's next enabled sibling.
8327     * @return YAHOO.widget.MenuItem
8328     */
8329     getNextEnabledSibling: function () {
8330         
8331         var next = this.getNextSibling();
8332         
8333         return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getNextEnabledSibling() : next;
8334         
8335     },
8336
8337
8338     /**
8339     * @method getPreviousSibling
8340     * @description Finds the menu item's previous sibling.
8341     * @return {YAHOO.widget.MenuItem}
8342     */  
8343     getPreviousSibling: function () {
8344
8345         var isUL = function (el) {
8346                 return (el.nodeName.toLowerCase() === "ul");
8347             },
8348
8349             menuitemEl = this.element,
8350             next = Dom.getPreviousSibling(menuitemEl),
8351             parent,
8352             sibling,
8353             list;
8354         
8355         if (!next) {
8356             
8357             parent = menuitemEl.parentNode;
8358             sibling = Dom.getPreviousSiblingBy(parent, isUL);
8359             
8360             if (sibling) {
8361                 list = sibling;
8362             }
8363             else {
8364                 list = Dom.getLastChildBy(parent.parentNode, isUL);
8365             }
8366             
8367             next = Dom.getLastChild(list);
8368             
8369         }
8370
8371         return YAHOO.widget.MenuManager.getMenuItem(next.id);
8372         
8373     },
8374
8375
8376     /**
8377     * @method getPreviousEnabledSibling
8378     * @description Finds the menu item's previous enabled sibling.
8379     * @return {YAHOO.widget.MenuItem}
8380     */
8381     getPreviousEnabledSibling: function () {
8382         
8383         var next = this.getPreviousSibling();
8384         
8385         return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getPreviousEnabledSibling() : next;
8386         
8387     },
8388
8389
8390     /**
8391     * @method focus
8392     * @description Causes the menu item to receive the focus and fires the 
8393     * focus event.
8394     */
8395     focus: function () {
8396
8397         var oParent = this.parent,
8398             oAnchor = this._oAnchor,
8399             oActiveItem = oParent.activeItem;
8400
8401
8402         function setFocus() {
8403
8404             try {
8405
8406                 if (!(UA.ie && !document.hasFocus())) {
8407                 
8408                     if (oActiveItem) {
8409         
8410                         oActiveItem.blurEvent.fire();
8411         
8412                     }
8413     
8414                     oAnchor.focus();
8415                     
8416                     this.focusEvent.fire();
8417                 
8418                 }
8419
8420             }
8421             catch(e) {
8422             
8423             }
8424
8425         }
8426
8427
8428         if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE) && 
8429             this.element.style.display != _NONE) {
8430
8431
8432             /*
8433                 Setting focus via a timer fixes a race condition in Firefox, IE 
8434                 and Opera where the browser viewport jumps as it trys to 
8435                 position and focus the menu.
8436             */
8437
8438             Lang.later(0, this, setFocus);
8439
8440         }
8441
8442     },
8443
8444
8445     /**
8446     * @method blur
8447     * @description Causes the menu item to lose focus and fires the 
8448     * blur event.
8449     */    
8450     blur: function () {
8451
8452         var oParent = this.parent;
8453
8454         if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE)) {
8455
8456             Lang.later(0, this, function () {
8457
8458                 try {
8459     
8460                     this._oAnchor.blur();
8461                     this.blurEvent.fire();    
8462
8463                 } 
8464                 catch (e) {
8465                 
8466                 }
8467                 
8468             }, 0);
8469
8470         }
8471
8472     },
8473
8474
8475     /**
8476     * @method hasFocus
8477     * @description Returns a boolean indicating whether or not the menu item
8478     * has focus.
8479     * @return {Boolean}
8480     */
8481     hasFocus: function () {
8482     
8483         return (YAHOO.widget.MenuManager.getFocusedMenuItem() == this);
8484     
8485     },
8486
8487
8488     /**
8489     * @method destroy
8490     * @description Removes the menu item's <code>&#60;li&#62;</code> element 
8491     * from its parent <code>&#60;ul&#62;</code> element.
8492     */
8493     destroy: function () {
8494
8495         var oEl = this.element,
8496             oSubmenu,
8497             oParentNode,
8498             aEventData,
8499             i;
8500
8501
8502         if (oEl) {
8503
8504
8505             // If the item has a submenu, destroy it first
8506
8507             oSubmenu = this.cfg.getProperty(_SUBMENU);
8508
8509             if (oSubmenu) {
8510             
8511                 oSubmenu.destroy();
8512             
8513             }
8514
8515
8516             // Remove the element from the parent node
8517
8518             oParentNode = oEl.parentNode;
8519
8520             if (oParentNode) {
8521
8522                 oParentNode.removeChild(oEl);
8523
8524                 this.destroyEvent.fire();
8525
8526             }
8527
8528
8529             // Remove CustomEvent listeners
8530
8531             i = EVENT_TYPES.length - 1;
8532
8533             do {
8534
8535                 aEventData = EVENT_TYPES[i];
8536                 
8537                 this[aEventData[0]].unsubscribeAll();
8538
8539             }
8540             while (i--);
8541             
8542             
8543             this.cfg.configChangedEvent.unsubscribeAll();
8544
8545         }
8546
8547     },
8548
8549
8550     /**
8551     * @method toString
8552     * @description Returns a string representing the menu item.
8553     * @return {String}
8554     */
8555     toString: function () {
8556
8557         var sReturnVal = _MENUITEM,
8558             sId = this.id;
8559
8560         if (sId) {
8561     
8562             sReturnVal += (_SPACE + sId);
8563         
8564         }
8565
8566         return sReturnVal;
8567     
8568     }
8569
8570 };
8571
8572 Lang.augmentProto(MenuItem, YAHOO.util.EventProvider);
8573
8574 })();
8575 (function () {
8576
8577     var _XY = "xy",
8578         _MOUSEDOWN = "mousedown",
8579         _CONTEXTMENU = "ContextMenu",
8580         _SPACE = " ";
8581
8582 /**
8583 * Creates a list of options or commands which are made visible in response to 
8584 * an HTML element's "contextmenu" event ("mousedown" for Opera).
8585 *
8586 * @param {String} p_oElement String specifying the id attribute of the 
8587 * <code>&#60;div&#62;</code> element of the context menu.
8588 * @param {String} p_oElement String specifying the id attribute of the 
8589 * <code>&#60;select&#62;</code> element to be used as the data source for the 
8590 * context menu.
8591 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8592 * html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 
8593 * <code>&#60;div&#62;</code> element of the context menu.
8594 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8595 * html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 
8596 * the <code>&#60;select&#62;</code> element to be used as the data source for 
8597 * the context menu.
8598 * @param {Object} p_oConfig Optional. Object literal specifying the 
8599 * configuration for the context menu. See configuration class documentation 
8600 * for more details.
8601 * @class ContextMenu
8602 * @constructor
8603 * @extends YAHOO.widget.Menu
8604 * @namespace YAHOO.widget
8605 */
8606 YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) {
8607     YAHOO.widget.ContextMenu.superclass.constructor.call(this, p_oElement, p_oConfig);
8608 };
8609
8610
8611 var Event = YAHOO.util.Event,
8612     UA = YAHOO.env.ua,
8613     ContextMenu = YAHOO.widget.ContextMenu,
8614
8615
8616
8617     /**
8618     * Constant representing the name of the ContextMenu's events
8619     * @property EVENT_TYPES
8620     * @private
8621     * @final
8622     * @type Object
8623     */
8624     EVENT_TYPES = {
8625
8626         "TRIGGER_CONTEXT_MENU": "triggerContextMenu",
8627         "CONTEXT_MENU": (UA.opera ? _MOUSEDOWN : "contextmenu"),
8628         "CLICK": "click"
8629
8630     },
8631     
8632     
8633     /**
8634     * Constant representing the ContextMenu's configuration properties
8635     * @property DEFAULT_CONFIG
8636     * @private
8637     * @final
8638     * @type Object
8639     */
8640     TRIGGER_CONFIG = { 
8641         key: "trigger",
8642         suppressEvent: true
8643     };
8644
8645
8646 /**
8647 * @method position
8648 * @description "beforeShow" event handler used to position the contextmenu.
8649 * @private
8650 * @param {String} p_sType String representing the name of the event that 
8651 * was fired.
8652 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
8653 * @param {Array} p_aPos Array representing the xy position for the context menu.
8654 */
8655 function position(p_sType, p_aArgs, p_aPos) {
8656     this.cfg.setProperty(_XY, p_aPos);
8657     this.beforeShowEvent.unsubscribe(position, p_aPos);
8658 }
8659
8660
8661 YAHOO.lang.extend(ContextMenu, YAHOO.widget.Menu, {
8662
8663
8664
8665 // Private properties
8666
8667
8668 /**
8669 * @property _oTrigger
8670 * @description Object reference to the current value of the "trigger" 
8671 * configuration property.
8672 * @default null
8673 * @private
8674 * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/leve
8675 * l-one-html.html#ID-58190037">HTMLElement</a>|Array
8676 */
8677 _oTrigger: null,
8678
8679
8680 /**
8681 * @property _bCancelled
8682 * @description Boolean indicating if the display of the context menu should 
8683 * be cancelled.
8684 * @default false
8685 * @private
8686 * @type Boolean
8687 */
8688 _bCancelled: false,
8689
8690
8691
8692 // Public properties
8693
8694
8695 /**
8696 * @property contextEventTarget
8697 * @description Object reference for the HTML element that was the target of the
8698 * "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of 
8699 * the context menu.
8700 * @default null
8701 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8702 * html.html#ID-58190037">HTMLElement</a>
8703 */
8704 contextEventTarget: null,
8705
8706
8707
8708 // Events
8709
8710
8711 /**
8712 * @event triggerContextMenuEvent
8713 * @param type {String} The name of the event, "triggerContextMenu"
8714 * @param args {Array} The array of event arguments. For this event, the underlying
8715 * DOM event is the only argument, available from args[0].
8716 * @description Custom Event wrapper for the "contextmenu" DOM event 
8717 * ("mousedown" for Opera) fired by the element(s) that trigger the display of 
8718 * the context menu.
8719 */
8720 triggerContextMenuEvent: null,
8721
8722
8723
8724 /**
8725 * @method init
8726 * @description The ContextMenu class's initialization method. This method is 
8727 * automatically called by the constructor, and sets up all DOM references for 
8728 * pre-existing markup, and creates required markup if it is not already present.
8729 * @param {String} p_oElement String specifying the id attribute of the 
8730 * <code>&#60;div&#62;</code> element of the context menu.
8731 * @param {String} p_oElement String specifying the id attribute of the 
8732 * <code>&#60;select&#62;</code> element to be used as the data source for 
8733 * the context menu.
8734 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8735 * html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 
8736 * <code>&#60;div&#62;</code> element of the context menu.
8737 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8738 * html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 
8739 * the <code>&#60;select&#62;</code> element to be used as the data source for 
8740 * the context menu.
8741 * @param {Object} p_oConfig Optional. Object literal specifying the 
8742 * configuration for the context menu. See configuration class documentation 
8743 * for more details.
8744 */
8745 init: function(p_oElement, p_oConfig) {
8746
8747
8748     // Call the init of the superclass (YAHOO.widget.Menu)
8749     
8750     ContextMenu.superclass.init.call(this, p_oElement);
8751
8752     this.beforeInitEvent.fire(ContextMenu);
8753
8754     if (p_oConfig) {
8755         this.cfg.applyConfig(p_oConfig, true);
8756     }
8757
8758     this.initEvent.fire(ContextMenu);
8759 },
8760
8761
8762 /**
8763 * @method initEvents
8764 * @description Initializes the custom events for the context menu.
8765 */
8766 initEvents: function() {
8767     ContextMenu.superclass.initEvents.call(this);
8768
8769     // Create custom events
8770     this.triggerContextMenuEvent = this.createEvent(EVENT_TYPES.TRIGGER_CONTEXT_MENU);
8771     this.triggerContextMenuEvent.signature = YAHOO.util.CustomEvent.LIST;
8772 },
8773
8774 /**
8775 * @method cancel
8776 * @description Cancels the display of the context menu.
8777 */
8778 cancel: function() {
8779     this._bCancelled = true;
8780 },
8781
8782 // Private methods
8783
8784
8785 /**
8786 * @method _removeEventHandlers
8787 * @description Removes all of the DOM event handlers from the HTML element(s) 
8788 * whose "context menu" event ("click" for Opera) trigger the display of 
8789 * the context menu.
8790 * @private
8791 */
8792 _removeEventHandlers: function() {
8793
8794     var oTrigger = this._oTrigger;
8795
8796     // Remove the event handlers from the trigger(s)
8797     if (oTrigger) {
8798         Event.removeListener(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu);    
8799
8800         if (UA.opera) {
8801             Event.removeListener(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick);
8802         }
8803     }
8804
8805 },
8806
8807 // Private event handlers
8808
8809 /**
8810 * @method _onTriggerClick
8811 * @description "click" event handler for the HTML element(s) identified as the 
8812 * "trigger" for the context menu.  Used to cancel default behaviors in Opera.
8813 * @private
8814 * @param {Event} p_oEvent Object representing the DOM event object passed back 
8815 * by the event utility (YAHOO.util.Event).
8816 * @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
8817 * menu that is handling the event.
8818 */
8819 _onTriggerClick: function(p_oEvent, p_oMenu) {
8820
8821     if (p_oEvent.ctrlKey) {
8822         Event.stopEvent(p_oEvent);
8823     }
8824     
8825 },
8826
8827
8828 /**
8829 * @method _onTriggerContextMenu
8830 * @description "contextmenu" event handler ("mousedown" for Opera) for the HTML 
8831 * element(s) that trigger the display of the context menu.
8832 * @private
8833 * @param {Event} p_oEvent Object representing the DOM event object passed back 
8834 * by the event utility (YAHOO.util.Event).
8835 * @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
8836 * menu that is handling the event.
8837 */
8838 _onTriggerContextMenu: function(p_oEvent, p_oMenu) {
8839
8840     var aXY;
8841
8842     if (!(p_oEvent.type == _MOUSEDOWN && !p_oEvent.ctrlKey)) {
8843     
8844         this.contextEventTarget = Event.getTarget(p_oEvent);
8845     
8846         this.triggerContextMenuEvent.fire(p_oEvent);
8847         
8848     
8849         if (!this._bCancelled) {
8850
8851             /*
8852                 Prevent the browser's default context menu from appearing and 
8853                 stop the propagation of the "contextmenu" event so that 
8854                 other ContextMenu instances are not displayed.
8855             */
8856
8857             Event.stopEvent(p_oEvent);
8858
8859
8860             // Hide any other Menu instances that might be visible
8861
8862             YAHOO.widget.MenuManager.hideVisible();
8863             
8864     
8865
8866             // Position and display the context menu
8867     
8868             aXY = Event.getXY(p_oEvent);
8869     
8870     
8871             if (!YAHOO.util.Dom.inDocument(this.element)) {
8872     
8873                 this.beforeShowEvent.subscribe(position, aXY);
8874     
8875             }
8876             else {
8877     
8878                 this.cfg.setProperty(_XY, aXY);
8879             
8880             }
8881     
8882     
8883             this.show();
8884     
8885         }
8886     
8887         this._bCancelled = false;
8888
8889     }
8890
8891 },
8892
8893
8894
8895 // Public methods
8896
8897
8898 /**
8899 * @method toString
8900 * @description Returns a string representing the context menu.
8901 * @return {String}
8902 */
8903 toString: function() {
8904
8905     var sReturnVal = _CONTEXTMENU,
8906         sId = this.id;
8907
8908     if (sId) {
8909
8910         sReturnVal += (_SPACE + sId);
8911     
8912     }
8913
8914     return sReturnVal;
8915
8916 },
8917
8918
8919 /**
8920 * @method initDefaultConfig
8921 * @description Initializes the class's configurable properties which can be 
8922 * changed using the context menu's Config object ("cfg").
8923 */
8924 initDefaultConfig: function() {
8925
8926     ContextMenu.superclass.initDefaultConfig.call(this);
8927
8928     /**
8929     * @config trigger
8930     * @description The HTML element(s) whose "contextmenu" event ("mousedown" 
8931     * for Opera) trigger the display of the context menu.  Can be a string 
8932     * representing the id attribute of the HTML element, an object reference 
8933     * for the HTML element, or an array of strings or HTML element references.
8934     * @default null
8935     * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
8936     * level-one-html.html#ID-58190037">HTMLElement</a>|Array
8937     */
8938     this.cfg.addProperty(TRIGGER_CONFIG.key, 
8939         {
8940             handler: this.configTrigger, 
8941             suppressEvent: TRIGGER_CONFIG.suppressEvent 
8942         }
8943     );
8944
8945 },
8946
8947
8948 /**
8949 * @method destroy
8950 * @description Removes the context menu's <code>&#60;div&#62;</code> element 
8951 * (and accompanying child nodes) from the document.
8952 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 
8953 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0.
8954 */
8955 destroy: function(shallowPurge) {
8956
8957     // Remove the DOM event handlers from the current trigger(s)
8958
8959     this._removeEventHandlers();
8960
8961
8962     // Continue with the superclass implementation of this method
8963
8964     ContextMenu.superclass.destroy.call(this, shallowPurge);
8965
8966 },
8967
8968
8969
8970 // Public event handlers for configuration properties
8971
8972
8973 /**
8974 * @method configTrigger
8975 * @description Event handler for when the value of the "trigger" configuration 
8976 * property changes. 
8977 * @param {String} p_sType String representing the name of the event that 
8978 * was fired.
8979 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
8980 * @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
8981 * menu that fired the event.
8982 */
8983 configTrigger: function(p_sType, p_aArgs, p_oMenu) {
8984     
8985     var oTrigger = p_aArgs[0];
8986
8987     if (oTrigger) {
8988
8989         /*
8990             If there is a current "trigger" - remove the event handlers 
8991             from that element(s) before assigning new ones
8992         */
8993
8994         if (this._oTrigger) {
8995         
8996             this._removeEventHandlers();
8997
8998         }
8999
9000         this._oTrigger = oTrigger;
9001
9002
9003         /*
9004             Listen for the "mousedown" event in Opera b/c it does not 
9005             support the "contextmenu" event
9006         */ 
9007   
9008         Event.on(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu, this, true);
9009
9010
9011         /*
9012             Assign a "click" event handler to the trigger element(s) for
9013             Opera to prevent default browser behaviors.
9014         */
9015
9016         if (UA.opera) {
9017         
9018             Event.on(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick, this, true);
9019
9020         }
9021
9022     }
9023     else {
9024    
9025         this._removeEventHandlers();
9026     
9027     }
9028     
9029 }
9030
9031 }); // END YAHOO.lang.extend
9032
9033 }());
9034
9035
9036
9037 /**
9038 * Creates an item for a context menu.
9039
9040 * @param {String} p_oObject String specifying the text of the context menu item.
9041 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9042 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
9043 * <code>&#60;li&#62;</code> element of the context menu item.
9044 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9045 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
9046 * specifying the <code>&#60;optgroup&#62;</code> element of the context 
9047 * menu item.
9048 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9049 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
9050 * the <code>&#60;option&#62;</code> element of the context menu item.
9051 * @param {Object} p_oConfig Optional. Object literal specifying the 
9052 * configuration for the context menu item. See configuration class 
9053 * documentation for more details.
9054 * @class ContextMenuItem
9055 * @constructor
9056 * @extends YAHOO.widget.MenuItem
9057 * @deprecated As of version 2.4.0 items for YAHOO.widget.ContextMenu instances
9058 * are of type YAHOO.widget.MenuItem.
9059 */
9060 YAHOO.widget.ContextMenuItem = YAHOO.widget.MenuItem;
9061 (function () {
9062
9063     var Lang = YAHOO.lang,
9064
9065         // String constants
9066     
9067         _STATIC = "static",
9068         _DYNAMIC_STATIC = "dynamic," + _STATIC,
9069         _DISABLED = "disabled",
9070         _SELECTED = "selected",
9071         _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay",
9072         _SUBMENU = "submenu",
9073         _VISIBLE = "visible",
9074         _SPACE = " ",
9075         _SUBMENU_TOGGLE_REGION = "submenutoggleregion",
9076         _MENUBAR = "MenuBar";
9077
9078 /**
9079 * Horizontal collection of items, each of which can contain a submenu.
9080
9081 * @param {String} p_oElement String specifying the id attribute of the 
9082 * <code>&#60;div&#62;</code> element of the menu bar.
9083 * @param {String} p_oElement String specifying the id attribute of the 
9084 * <code>&#60;select&#62;</code> element to be used as the data source for the 
9085 * menu bar.
9086 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9087 * one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 
9088 * the <code>&#60;div&#62;</code> element of the menu bar.
9089 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9090 * one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 
9091 * specifying the <code>&#60;select&#62;</code> element to be used as the data 
9092 * source for the menu bar.
9093 * @param {Object} p_oConfig Optional. Object literal specifying the 
9094 * configuration for the menu bar. See configuration class documentation for
9095 * more details.
9096 * @class MenuBar
9097 * @constructor
9098 * @extends YAHOO.widget.Menu
9099 * @namespace YAHOO.widget
9100 */
9101 YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) {
9102
9103     YAHOO.widget.MenuBar.superclass.constructor.call(this, p_oElement, p_oConfig);
9104
9105 };
9106
9107
9108 /**
9109 * @method checkPosition
9110 * @description Checks to make sure that the value of the "position" property 
9111 * is one of the supported strings. Returns true if the position is supported.
9112 * @private
9113 * @param {Object} p_sPosition String specifying the position of the menu.
9114 * @return {Boolean}
9115 */
9116 function checkPosition(p_sPosition) {
9117
9118     var returnVal = false;
9119
9120     if (Lang.isString(p_sPosition)) {
9121
9122         returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1);
9123
9124     }
9125     
9126     return returnVal;
9127
9128 }
9129
9130
9131 var Event = YAHOO.util.Event,
9132     MenuBar = YAHOO.widget.MenuBar,
9133
9134     POSITION_CONFIG =  { 
9135         key: "position", 
9136         value: _STATIC, 
9137         validator: checkPosition, 
9138         supercedes: [_VISIBLE] 
9139     }, 
9140
9141     SUBMENU_ALIGNMENT_CONFIG =  { 
9142         key: "submenualignment", 
9143         value: ["tl","bl"]
9144     },
9145
9146     AUTO_SUBMENU_DISPLAY_CONFIG =  { 
9147         key: _AUTO_SUBMENU_DISPLAY, 
9148         value: false, 
9149         validator: Lang.isBoolean,
9150         suppressEvent: true
9151     },
9152     
9153     SUBMENU_TOGGLE_REGION_CONFIG = {
9154         key: _SUBMENU_TOGGLE_REGION, 
9155         value: false, 
9156         validator: Lang.isBoolean
9157     };
9158
9159
9160
9161 Lang.extend(MenuBar, YAHOO.widget.Menu, {
9162
9163 /**
9164 * @method init
9165 * @description The MenuBar class's initialization method. This method is 
9166 * automatically called by the constructor, and sets up all DOM references for 
9167 * pre-existing markup, and creates required markup if it is not already present.
9168 * @param {String} p_oElement String specifying the id attribute of the 
9169 * <code>&#60;div&#62;</code> element of the menu bar.
9170 * @param {String} p_oElement String specifying the id attribute of the 
9171 * <code>&#60;select&#62;</code> element to be used as the data source for the 
9172 * menu bar.
9173 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9174 * one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 
9175 * the <code>&#60;div&#62;</code> element of the menu bar.
9176 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9177 * one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 
9178 * specifying the <code>&#60;select&#62;</code> element to be used as the data 
9179 * source for the menu bar.
9180 * @param {Object} p_oConfig Optional. Object literal specifying the 
9181 * configuration for the menu bar. See configuration class documentation for
9182 * more details.
9183 */
9184 init: function(p_oElement, p_oConfig) {
9185
9186     if(!this.ITEM_TYPE) {
9187
9188         this.ITEM_TYPE = YAHOO.widget.MenuBarItem;
9189
9190     }
9191
9192
9193     // Call the init of the superclass (YAHOO.widget.Menu)
9194
9195     MenuBar.superclass.init.call(this, p_oElement);
9196
9197
9198     this.beforeInitEvent.fire(MenuBar);
9199
9200
9201     if(p_oConfig) {
9202
9203         this.cfg.applyConfig(p_oConfig, true);
9204
9205     }
9206
9207     this.initEvent.fire(MenuBar);
9208
9209 },
9210
9211
9212
9213 // Constants
9214
9215
9216 /**
9217 * @property CSS_CLASS_NAME
9218 * @description String representing the CSS class(es) to be applied to the menu 
9219 * bar's <code>&#60;div&#62;</code> element.
9220 * @default "yuimenubar"
9221 * @final
9222 * @type String
9223 */
9224 CSS_CLASS_NAME: "yuimenubar",
9225
9226
9227 /**
9228 * @property SUBMENU_TOGGLE_REGION_WIDTH
9229 * @description Width (in pixels) of the area of a MenuBarItem that, when pressed, will toggle the
9230 * display of the MenuBarItem's submenu.
9231 * @default 20
9232 * @final
9233 * @type Number
9234 */
9235 SUBMENU_TOGGLE_REGION_WIDTH: 20,
9236
9237
9238 // Protected event handlers
9239
9240
9241 /**
9242 * @method _onKeyDown
9243 * @description "keydown" Custom Event handler for the menu bar.
9244 * @private
9245 * @param {String} p_sType String representing the name of the event that 
9246 * was fired.
9247 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
9248 * @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 
9249 * that fired the event.
9250 */
9251 _onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) {
9252
9253     var oEvent = p_aArgs[0],
9254         oItem = p_aArgs[1],
9255         oSubmenu,
9256         oItemCfg,
9257         oNextItem;
9258
9259
9260     if(oItem && !oItem.cfg.getProperty(_DISABLED)) {
9261
9262         oItemCfg = oItem.cfg;
9263
9264         switch(oEvent.keyCode) {
9265     
9266             case 37:    // Left arrow
9267             case 39:    // Right arrow
9268     
9269                 if(oItem == this.activeItem && !oItemCfg.getProperty(_SELECTED)) {
9270     
9271                     oItemCfg.setProperty(_SELECTED, true);
9272     
9273                 }
9274                 else {
9275     
9276                     oNextItem = (oEvent.keyCode == 37) ? 
9277                         oItem.getPreviousEnabledSibling() : 
9278                         oItem.getNextEnabledSibling();
9279             
9280                     if(oNextItem) {
9281     
9282                         this.clearActiveItem();
9283     
9284                         oNextItem.cfg.setProperty(_SELECTED, true);
9285                         
9286                         oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
9287                         
9288                         if(oSubmenu) {
9289                     
9290                             oSubmenu.show();
9291                             oSubmenu.setInitialFocus();
9292                         
9293                         }
9294                         else {
9295                             oNextItem.focus();  
9296                         }
9297     
9298                     }
9299     
9300                 }
9301     
9302                 Event.preventDefault(oEvent);
9303     
9304             break;
9305     
9306             case 40:    // Down arrow
9307     
9308                 if(this.activeItem != oItem) {
9309     
9310                     this.clearActiveItem();
9311     
9312                     oItemCfg.setProperty(_SELECTED, true);
9313                     oItem.focus();
9314                 
9315                 }
9316     
9317                 oSubmenu = oItemCfg.getProperty(_SUBMENU);
9318     
9319                 if(oSubmenu) {
9320     
9321                     if(oSubmenu.cfg.getProperty(_VISIBLE)) {
9322     
9323                         oSubmenu.setInitialSelection();
9324                         oSubmenu.setInitialFocus();
9325                     
9326                     }
9327                     else {
9328     
9329                         oSubmenu.show();
9330                         oSubmenu.setInitialFocus();
9331                     
9332                     }
9333     
9334                 }
9335     
9336                 Event.preventDefault(oEvent);
9337     
9338             break;
9339     
9340         }
9341
9342     }
9343
9344
9345     if(oEvent.keyCode == 27 && this.activeItem) { // Esc key
9346
9347         oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU);
9348
9349         if(oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) {
9350         
9351             oSubmenu.hide();
9352             this.activeItem.focus();
9353         
9354         }
9355         else {
9356
9357             this.activeItem.cfg.setProperty(_SELECTED, false);
9358             this.activeItem.blur();
9359     
9360         }
9361
9362         Event.preventDefault(oEvent);
9363     
9364     }
9365
9366 },
9367
9368
9369 /**
9370 * @method _onClick
9371 * @description "click" event handler for the menu bar.
9372 * @protected
9373 * @param {String} p_sType String representing the name of the event that 
9374 * was fired.
9375 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
9376 * @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 
9377 * that fired the event.
9378 */
9379 _onClick: function(p_sType, p_aArgs, p_oMenuBar) {
9380
9381     MenuBar.superclass._onClick.call(this, p_sType, p_aArgs, p_oMenuBar);
9382
9383     var oItem = p_aArgs[1],
9384         bReturnVal = true,
9385         oItemEl,
9386         oEvent,
9387         oTarget,
9388         oActiveItem,
9389         oConfig,
9390         oSubmenu,
9391         nMenuItemX,
9392         nToggleRegion;
9393
9394
9395     var toggleSubmenuDisplay = function () {
9396
9397         if(oSubmenu.cfg.getProperty(_VISIBLE)) {
9398         
9399             oSubmenu.hide();
9400         
9401         }
9402         else {
9403         
9404             oSubmenu.show();                    
9405         
9406         }
9407     
9408     };
9409     
9410
9411     if(oItem && !oItem.cfg.getProperty(_DISABLED)) {
9412
9413         oEvent = p_aArgs[0];
9414         oTarget = Event.getTarget(oEvent);
9415         oActiveItem = this.activeItem;
9416         oConfig = this.cfg;
9417
9418
9419         // Hide any other submenus that might be visible
9420     
9421         if(oActiveItem && oActiveItem != oItem) {
9422     
9423             this.clearActiveItem();
9424     
9425         }
9426
9427     
9428         oItem.cfg.setProperty(_SELECTED, true);
9429     
9430
9431         // Show the submenu for the item
9432     
9433         oSubmenu = oItem.cfg.getProperty(_SUBMENU);
9434
9435
9436         if(oSubmenu) {
9437
9438             oItemEl = oItem.element;
9439             nMenuItemX = YAHOO.util.Dom.getX(oItemEl);
9440             nToggleRegion = nMenuItemX + (oItemEl.offsetWidth - this.SUBMENU_TOGGLE_REGION_WIDTH);
9441
9442             if (oConfig.getProperty(_SUBMENU_TOGGLE_REGION)) {
9443
9444                 if (Event.getPageX(oEvent) > nToggleRegion) {
9445
9446                     toggleSubmenuDisplay();
9447
9448                     Event.preventDefault(oEvent);
9449
9450                     /*
9451                          Return false so that other click event handlers are not called when the 
9452                          user clicks inside the toggle region.
9453                     */
9454                     bReturnVal = false;
9455                 
9456                 }
9457         
9458             }
9459             else {
9460
9461                 toggleSubmenuDisplay();
9462             
9463             }
9464         
9465         }
9466     
9467     }
9468
9469
9470     return bReturnVal;
9471
9472 },
9473
9474
9475
9476 // Public methods
9477
9478 /**
9479 * @method configSubmenuToggle
9480 * @description Event handler for when the "submenutoggleregion" configuration property of 
9481 * a MenuBar changes.
9482 * @param {String} p_sType The name of the event that was fired.
9483 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
9484 */
9485 configSubmenuToggle: function (p_sType, p_aArgs) {
9486
9487     var bSubmenuToggle = p_aArgs[0];
9488     
9489     if (bSubmenuToggle) {
9490     
9491         this.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false);
9492     
9493     }
9494
9495 },
9496
9497
9498 /**
9499 * @method toString
9500 * @description Returns a string representing the menu bar.
9501 * @return {String}
9502 */
9503 toString: function() {
9504
9505     var sReturnVal = _MENUBAR,
9506         sId = this.id;
9507
9508     if(sId) {
9509
9510         sReturnVal += (_SPACE + sId);
9511     
9512     }
9513
9514     return sReturnVal;
9515
9516 },
9517
9518
9519 /**
9520 * @description Initializes the class's configurable properties which can be
9521 * changed using the menu bar's Config object ("cfg").
9522 * @method initDefaultConfig
9523 */
9524 initDefaultConfig: function() {
9525
9526     MenuBar.superclass.initDefaultConfig.call(this);
9527
9528     var oConfig = this.cfg;
9529
9530     // Add configuration properties
9531
9532
9533     /*
9534         Set the default value for the "position" configuration property
9535         to "static" by re-adding the property.
9536     */
9537
9538
9539     /**
9540     * @config position
9541     * @description String indicating how a menu bar should be positioned on the 
9542     * screen.  Possible values are "static" and "dynamic."  Static menu bars 
9543     * are visible by default and reside in the normal flow of the document 
9544     * (CSS position: static).  Dynamic menu bars are hidden by default, reside
9545     * out of the normal flow of the document (CSS position: absolute), and can 
9546     * overlay other elements on the screen.
9547     * @default static
9548     * @type String
9549     */
9550     oConfig.addProperty(
9551         POSITION_CONFIG.key, 
9552         {
9553             handler: this.configPosition, 
9554             value: POSITION_CONFIG.value, 
9555             validator: POSITION_CONFIG.validator,
9556             supercedes: POSITION_CONFIG.supercedes
9557         }
9558     );
9559
9560
9561     /*
9562         Set the default value for the "submenualignment" configuration property
9563         to ["tl","bl"] by re-adding the property.
9564     */
9565
9566     /**
9567     * @config submenualignment
9568     * @description Array defining how submenus should be aligned to their 
9569     * parent menu bar item. The format is: [itemCorner, submenuCorner].
9570     * @default ["tl","bl"]
9571     * @type Array
9572     */
9573     oConfig.addProperty(
9574         SUBMENU_ALIGNMENT_CONFIG.key, 
9575         {
9576             value: SUBMENU_ALIGNMENT_CONFIG.value,
9577             suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent
9578         }
9579     );
9580
9581
9582     /*
9583         Change the default value for the "autosubmenudisplay" configuration 
9584         property to "false" by re-adding the property.
9585     */
9586
9587     /**
9588     * @config autosubmenudisplay
9589     * @description Boolean indicating if submenus are automatically made 
9590     * visible when the user mouses over the menu bar's items.
9591     * @default false
9592     * @type Boolean
9593     */
9594     oConfig.addProperty(
9595        AUTO_SUBMENU_DISPLAY_CONFIG.key, 
9596        {
9597            value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 
9598            validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator,
9599            suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent
9600        } 
9601     );
9602
9603
9604     /**
9605     * @config submenutoggleregion
9606     * @description Boolean indicating if only a specific region of a MenuBarItem should toggle the 
9607     * display of a submenu.  The default width of the region is determined by the value of the
9608     * SUBMENU_TOGGLE_REGION_WIDTH property.  If set to true, the autosubmenudisplay 
9609     * configuration property will be set to false, and any click event listeners will not be 
9610     * called when the user clicks inside the submenu toggle region of a MenuBarItem.  If the 
9611     * user clicks outside of the submenu toggle region, the MenuBarItem will maintain its 
9612     * standard behavior.
9613     * @default false
9614     * @type Boolean
9615     */
9616     oConfig.addProperty(
9617        SUBMENU_TOGGLE_REGION_CONFIG.key, 
9618        {
9619            value: SUBMENU_TOGGLE_REGION_CONFIG.value, 
9620            validator: SUBMENU_TOGGLE_REGION_CONFIG.validator,
9621            handler: this.configSubmenuToggle
9622        } 
9623     );
9624
9625 }
9626  
9627 }); // END YAHOO.lang.extend
9628
9629 }());
9630
9631
9632
9633 /**
9634 * Creates an item for a menu bar.
9635
9636 * @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
9637 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9638 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
9639 * <code>&#60;li&#62;</code> element of the menu bar item.
9640 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9641 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
9642 * specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
9643 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9644 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
9645 * the <code>&#60;option&#62;</code> element of the menu bar item.
9646 * @param {Object} p_oConfig Optional. Object literal specifying the 
9647 * configuration for the menu bar item. See configuration class documentation 
9648 * for more details.
9649 * @class MenuBarItem
9650 * @constructor
9651 * @extends YAHOO.widget.MenuItem
9652 */
9653 YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) {
9654
9655     YAHOO.widget.MenuBarItem.superclass.constructor.call(this, p_oObject, p_oConfig);
9656
9657 };
9658
9659 YAHOO.lang.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, {
9660
9661
9662
9663 /**
9664 * @method init
9665 * @description The MenuBarItem class's initialization method. This method is 
9666 * automatically called by the constructor, and sets up all DOM references for 
9667 * pre-existing markup, and creates required markup if it is not already present.
9668 * @param {HTML} p_oObject Markup for the menu item content. The markup is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
9669 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9670 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
9671 * <code>&#60;li&#62;</code> element of the menu bar item.
9672 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9673 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
9674 * specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
9675 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9676 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
9677 * the <code>&#60;option&#62;</code> element of the menu bar item.
9678 * @param {Object} p_oConfig Optional. Object literal specifying the 
9679 * configuration for the menu bar item. See configuration class documentation 
9680 * for more details.
9681 */
9682 init: function(p_oObject, p_oConfig) {
9683
9684     if(!this.SUBMENU_TYPE) {
9685
9686         this.SUBMENU_TYPE = YAHOO.widget.Menu;
9687
9688     }
9689
9690
9691     /* 
9692         Call the init of the superclass (YAHOO.widget.MenuItem)
9693         Note: We don't pass the user config in here yet 
9694         because we only want it executed once, at the lowest 
9695         subclass level.
9696     */ 
9697
9698     YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject);  
9699
9700
9701     var oConfig = this.cfg;
9702
9703     if(p_oConfig) {
9704
9705         oConfig.applyConfig(p_oConfig, true);
9706
9707     }
9708
9709     oConfig.fireQueue();
9710
9711 },
9712
9713
9714
9715 // Constants
9716
9717
9718 /**
9719 * @property CSS_CLASS_NAME
9720 * @description String representing the CSS class(es) to be applied to the 
9721 * <code>&#60;li&#62;</code> element of the menu bar item.
9722 * @default "yuimenubaritem"
9723 * @final
9724 * @type String
9725 */
9726 CSS_CLASS_NAME: "yuimenubaritem",
9727
9728
9729 /**
9730 * @property CSS_LABEL_CLASS_NAME
9731 * @description String representing the CSS class(es) to be applied to the 
9732 * menu bar item's <code>&#60;a&#62;</code> element.
9733 * @default "yuimenubaritemlabel"
9734 * @final
9735 * @type String
9736 */
9737 CSS_LABEL_CLASS_NAME: "yuimenubaritemlabel",
9738
9739
9740
9741 // Public methods
9742
9743
9744 /**
9745 * @method toString
9746 * @description Returns a string representing the menu bar item.
9747 * @return {String}
9748 */
9749 toString: function() {
9750
9751     var sReturnVal = "MenuBarItem";
9752
9753     if(this.cfg && this.cfg.getProperty("text")) {
9754
9755         sReturnVal += (": " + this.cfg.getProperty("text"));
9756
9757     }
9758
9759     return sReturnVal;
9760
9761 }
9762     
9763 }); // END YAHOO.lang.extend
9764 YAHOO.register("menu", YAHOO.widget.Menu, {version: "2.9.0", build: "2800"});