]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/menu/menu.js
Release 6.2.2
[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
3821         // The Menu is below the context element
3822         if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) {
3823             return (nBottomRegionHeight - nViewportOffset);                             
3824         }
3825         else {  // The Menu is above the context element
3826             return (nTopRegionHeight - nViewportOffset);                                
3827         }
3828
3829     };
3830
3831
3832     /*
3833         Sets the Menu's "y" configuration property to the correct value based on its
3834         current orientation.
3835     */ 
3836
3837     var alignY = function () {
3838
3839         var nNewY;
3840
3841         if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 
3842             nNewY = (nContextElY + nContextElHeight);
3843         }
3844         else {  
3845             nNewY = (nContextElY - oMenuEl.offsetHeight);
3846         }
3847
3848         oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true);
3849     
3850     };
3851
3852
3853     //  Resets the maxheight of the Menu to the value set by the user
3854
3855     var resetMaxHeight = function () {
3856
3857         oMenu._setScrollHeight(this.cfg.getProperty(_MAX_HEIGHT));
3858
3859         oMenu.hideEvent.unsubscribe(resetMaxHeight);
3860     
3861     };
3862
3863
3864     /*
3865         Trys to place the Menu in the best possible position (either above or 
3866         below its corresponding context element).
3867     */
3868
3869     var setVerticalPosition = function () {
3870
3871         var nDisplayRegionHeight = getDisplayRegionHeight(),
3872             bMenuHasItems = (oMenu.getItems().length > 0),
3873             nMenuMinScrollHeight,
3874             fnReturnVal;
3875
3876
3877         if (nMenuOffsetHeight > nDisplayRegionHeight) {
3878
3879             nMenuMinScrollHeight = 
3880                 bMenuHasItems ? oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) : nMenuOffsetHeight;
3881
3882
3883             if ((nDisplayRegionHeight > nMenuMinScrollHeight) && bMenuHasItems) {
3884                 nMaxHeight = nDisplayRegionHeight;
3885             }
3886             else {
3887                 nMaxHeight = nInitialMaxHeight;
3888             }
3889
3890
3891             oMenu._setScrollHeight(nMaxHeight);
3892             oMenu.hideEvent.subscribe(resetMaxHeight);
3893             
3894
3895             // Re-align the Menu since its height has just changed
3896             // as a result of the setting of the maxheight property.
3897
3898             alignY();
3899             
3900
3901             if (nDisplayRegionHeight < nMenuMinScrollHeight) {
3902
3903                 if (bFlipped) {
3904     
3905                     /*
3906                          All possible positions and values for the "maxheight" 
3907                          configuration property have been tried, but none were 
3908                          successful, so fall back to the original size and position.
3909                     */
3910
3911                     flipVertical();
3912                     
3913                 }
3914                 else {
3915     
3916                     flipVertical();
3917
3918                     bFlipped = true;
3919     
3920                     fnReturnVal = setVerticalPosition();
3921     
3922                 }
3923                 
3924             }
3925         
3926         }
3927         else if (nMaxHeight && (nMaxHeight !== nInitialMaxHeight)) {
3928         
3929             oMenu._setScrollHeight(nInitialMaxHeight);
3930             oMenu.hideEvent.subscribe(resetMaxHeight);
3931
3932             // Re-align the Menu since its height has just changed
3933             // as a result of the setting of the maxheight property.
3934
3935             alignY();
3936         
3937         }
3938
3939         return fnReturnVal;
3940
3941     };
3942
3943
3944     // Determine if the current value for the Menu's "y" configuration property will
3945     // result in the Menu being positioned outside the boundaries of the viewport
3946
3947     if (y < topConstraint || y  > bottomConstraint) {
3948
3949         // The current value for the Menu's "y" configuration property WILL
3950         // result in the Menu being positioned outside the boundaries of the viewport
3951
3952         if (bCanConstrain) {
3953
3954             if (oMenu.cfg.getProperty(_PREVENT_CONTEXT_OVERLAP) && bPotentialContextOverlap) {
3955         
3956                 //      SOLUTION #1:
3957                 //      If the "preventcontextoverlap" configuration property is set to "true", 
3958                 //      try to flip and/or scroll the Menu to both keep it inside the boundaries of the 
3959                 //      viewport AND from overlaping its context element (MenuItem or MenuBarItem).
3960
3961                 oContextEl = aContext[0];
3962                 nContextElHeight = oContextEl.offsetHeight;
3963                 nContextElY = (Dom.getY(oContextEl) - scrollY);
3964     
3965                 nTopRegionHeight = nContextElY;
3966                 nBottomRegionHeight = (viewPortHeight - (nContextElY + nContextElHeight));
3967     
3968                 setVerticalPosition();
3969                 
3970                 yNew = oMenu.cfg.getProperty(_Y);
3971         
3972             }
3973             else if (!(oMenu instanceof YAHOO.widget.MenuBar) && 
3974                 nMenuOffsetHeight >= viewPortHeight) {
3975
3976                 //      SOLUTION #2:
3977                 //      If the Menu exceeds the height of the viewport, introduce scroll bars
3978                 //      to keep the Menu inside the boundaries of the viewport
3979
3980                 nAvailableHeight = (viewPortHeight - (nViewportOffset * 2));
3981         
3982                 if (nAvailableHeight > oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT)) {
3983         
3984                     oMenu._setScrollHeight(nAvailableHeight);
3985                     oMenu.hideEvent.subscribe(resetMaxHeight);
3986         
3987                     alignY();
3988                     
3989                     yNew = oMenu.cfg.getProperty(_Y);
3990                 
3991                 }
3992         
3993             }   
3994             else {
3995
3996                 //      SOLUTION #3:
3997             
3998                 if (y < topConstraint) {
3999                     yNew  = topConstraint;
4000                 } else if (y  > bottomConstraint) {
4001                     yNew  = bottomConstraint;
4002                 }                               
4003             
4004             }
4005
4006         }
4007         else {
4008             //  The "y" configuration property cannot be set to a value that will keep
4009             //  entire Menu inside the boundary of the viewport.  Therefore, set  
4010             //  the "y" configuration property to scrollY to keep as much of the 
4011             //  Menu inside the viewport as possible.
4012             yNew = nViewportOffset + scrollY;
4013         }       
4014
4015     }
4016
4017     return yNew;
4018
4019 },
4020
4021
4022 /**
4023 * @method _onHide
4024 * @description "hide" event handler for the menu.
4025 * @private
4026 * @param {String} p_sType String representing the name of the event that 
4027 * was fired.
4028 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4029 */
4030 _onHide: function (p_sType, p_aArgs) {
4031
4032     if (this.cfg.getProperty(_POSITION) === _DYNAMIC) {
4033     
4034         this.positionOffScreen();
4035     
4036     }
4037
4038 },
4039
4040
4041 /**
4042 * @method _onShow
4043 * @description "show" event handler for the menu.
4044 * @private
4045 * @param {String} p_sType String representing the name of the event that 
4046 * was fired.
4047 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4048 */
4049 _onShow: function (p_sType, p_aArgs) {
4050
4051     var oParent = this.parent,
4052         oParentMenu,
4053         oElement,
4054         nOffsetWidth,
4055         sWidth;        
4056
4057
4058     function disableAutoSubmenuDisplay(p_oEvent) {
4059
4060         var oTarget;
4061
4062         if (p_oEvent.type == _MOUSEDOWN || (p_oEvent.type == _KEYDOWN && p_oEvent.keyCode == 27)) {
4063
4064             /*  
4065                 Set the "autosubmenudisplay" to "false" if the user
4066                 clicks outside the menu bar.
4067             */
4068
4069             oTarget = Event.getTarget(p_oEvent);
4070
4071             if (oTarget != oParentMenu.element || !Dom.isAncestor(oParentMenu.element, oTarget)) {
4072
4073                 oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false);
4074
4075                 Event.removeListener(document, _MOUSEDOWN, disableAutoSubmenuDisplay);
4076                 Event.removeListener(document, _KEYDOWN, disableAutoSubmenuDisplay);
4077
4078             }
4079         
4080         }
4081
4082     }
4083
4084
4085     function onSubmenuHide(p_sType, p_aArgs, p_sWidth) {
4086     
4087         this.cfg.setProperty(_WIDTH, _EMPTY_STRING);
4088         this.hideEvent.unsubscribe(onSubmenuHide, p_sWidth);
4089     
4090     }
4091
4092
4093     if (oParent) {
4094
4095         oParentMenu = oParent.parent;
4096
4097
4098         if (!oParentMenu.cfg.getProperty(_AUTO_SUBMENU_DISPLAY) && 
4099             (oParentMenu instanceof YAHOO.widget.MenuBar || 
4100             oParentMenu.cfg.getProperty(_POSITION) == _STATIC)) {
4101
4102             oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, true);
4103
4104             Event.on(document, _MOUSEDOWN, disableAutoSubmenuDisplay);                             
4105             Event.on(document, _KEYDOWN, disableAutoSubmenuDisplay);
4106
4107         }
4108
4109
4110         //      The following fixes an issue with the selected state of a MenuItem 
4111         //      not rendering correctly when a submenu is aligned to the left of
4112         //      its parent Menu instance.
4113
4114         if ((this.cfg.getProperty("x") < oParentMenu.cfg.getProperty("x")) && 
4115             (UA.gecko && UA.gecko < 1.9) && !this.cfg.getProperty(_WIDTH)) {
4116
4117             oElement = this.element;
4118             nOffsetWidth = oElement.offsetWidth;
4119             
4120             /*
4121                 Measuring the difference of the offsetWidth before and after
4122                 setting the "width" style attribute allows us to compute the 
4123                 about of padding and borders applied to the element, which in 
4124                 turn allows us to set the "width" property correctly.
4125             */
4126             
4127             oElement.style.width = nOffsetWidth + _PX;
4128             
4129             sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX;
4130             
4131             this.cfg.setProperty(_WIDTH, sWidth);
4132         
4133             this.hideEvent.subscribe(onSubmenuHide, sWidth);
4134         
4135         }
4136
4137     }
4138
4139
4140     /*
4141         Dynamically positioned, root Menus focus themselves when visible, and 
4142         will then, when hidden, restore focus to the UI control that had focus 
4143         before the Menu was made visible.
4144     */ 
4145
4146     if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) {
4147
4148         this._focusedElement = oFocusedElement;
4149         
4150         this.focus();
4151     
4152     }
4153
4154
4155 },
4156
4157
4158 /**
4159 * @method _onBeforeHide
4160 * @description "beforehide" event handler for the menu.
4161 * @private
4162 * @param {String} p_sType String representing the name of the event that 
4163 * was fired.
4164 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4165 */
4166 _onBeforeHide: function (p_sType, p_aArgs) {
4167
4168     var oActiveItem = this.activeItem,
4169         oRoot = this.getRoot(),
4170         oConfig,
4171         oSubmenu;
4172
4173
4174     if (oActiveItem) {
4175
4176         oConfig = oActiveItem.cfg;
4177
4178         oConfig.setProperty(_SELECTED, false);
4179
4180         oSubmenu = oConfig.getProperty(_SUBMENU);
4181
4182         if (oSubmenu) {
4183
4184             oSubmenu.hide();
4185
4186         }
4187
4188     }
4189
4190
4191     /*
4192         Focus can get lost in IE when the mouse is moving from a submenu back to its parent Menu.  
4193         For this reason, it is necessary to maintain the focused state in a private property 
4194         so that the _onMouseOver event handler is able to determined whether or not to set focus
4195         to MenuItems as the user is moving the mouse.
4196     */ 
4197
4198     if (UA.ie && this.cfg.getProperty(_POSITION) === _DYNAMIC && this.parent) {
4199
4200         oRoot._hasFocus = this.hasFocus();
4201     
4202     }
4203
4204
4205     if (oRoot == this) {
4206
4207         oRoot.blur();
4208     
4209     }
4210
4211 },
4212
4213
4214 /**
4215 * @method _onParentMenuConfigChange
4216 * @description "configchange" event handler for a submenu.
4217 * @private
4218 * @param {String} p_sType String representing the name of the event that 
4219 * was fired.
4220 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4221 * @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 
4222 * subscribed to the event.
4223 */
4224 _onParentMenuConfigChange: function (p_sType, p_aArgs, p_oSubmenu) {
4225     
4226     var sPropertyName = p_aArgs[0][0],
4227         oPropertyValue = p_aArgs[0][1];
4228
4229     switch(sPropertyName) {
4230
4231         case _IFRAME:
4232         case _CONSTRAIN_TO_VIEWPORT:
4233         case _HIDE_DELAY:
4234         case _SHOW_DELAY:
4235         case _SUBMENU_HIDE_DELAY:
4236         case _CLICK_TO_HIDE:
4237         case _EFFECT:
4238         case _CLASSNAME:
4239         case _SCROLL_INCREMENT:
4240         case _MAX_HEIGHT:
4241         case _MIN_SCROLL_HEIGHT:
4242         case _MONITOR_RESIZE:
4243         case _SHADOW:
4244         case _PREVENT_CONTEXT_OVERLAP:
4245         case _KEEP_OPEN:
4246
4247             p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
4248                 
4249         break;
4250         
4251         case _SUBMENU_ALIGNMENT:
4252
4253             if (!(this.parent.parent instanceof YAHOO.widget.MenuBar)) {
4254         
4255                 p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
4256         
4257             }
4258         
4259         break;
4260         
4261     }
4262     
4263 },
4264
4265
4266 /**
4267 * @method _onParentMenuRender
4268 * @description "render" event handler for a submenu.  Renders a  
4269 * submenu in response to the firing of its parent's "render" event.
4270 * @private
4271 * @param {String} p_sType String representing the name of the event that 
4272 * was fired.
4273 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4274 * @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 
4275 * subscribed to the event.
4276 */
4277 _onParentMenuRender: function (p_sType, p_aArgs, p_oSubmenu) {
4278
4279     var oParentMenu = p_oSubmenu.parent.parent,
4280         oParentCfg = oParentMenu.cfg,
4281
4282         oConfig = {
4283
4284             constraintoviewport: oParentCfg.getProperty(_CONSTRAIN_TO_VIEWPORT),
4285
4286             xy: [0,0],
4287
4288             clicktohide: oParentCfg.getProperty(_CLICK_TO_HIDE),
4289                 
4290             effect: oParentCfg.getProperty(_EFFECT),
4291
4292             showdelay: oParentCfg.getProperty(_SHOW_DELAY),
4293             
4294             hidedelay: oParentCfg.getProperty(_HIDE_DELAY),
4295
4296             submenuhidedelay: oParentCfg.getProperty(_SUBMENU_HIDE_DELAY),
4297
4298             classname: oParentCfg.getProperty(_CLASSNAME),
4299             
4300             scrollincrement: oParentCfg.getProperty(_SCROLL_INCREMENT),
4301             
4302             maxheight: oParentCfg.getProperty(_MAX_HEIGHT),
4303
4304             minscrollheight: oParentCfg.getProperty(_MIN_SCROLL_HEIGHT),
4305             
4306             iframe: oParentCfg.getProperty(_IFRAME),
4307             
4308             shadow: oParentCfg.getProperty(_SHADOW),
4309
4310             preventcontextoverlap: oParentCfg.getProperty(_PREVENT_CONTEXT_OVERLAP),
4311             
4312             monitorresize: oParentCfg.getProperty(_MONITOR_RESIZE),
4313
4314             keepopen: oParentCfg.getProperty(_KEEP_OPEN)
4315
4316         },
4317         
4318         oLI;
4319
4320
4321     
4322     if (!(oParentMenu instanceof YAHOO.widget.MenuBar)) {
4323
4324         oConfig[_SUBMENU_ALIGNMENT] = oParentCfg.getProperty(_SUBMENU_ALIGNMENT);
4325
4326     }
4327
4328
4329     p_oSubmenu.cfg.applyConfig(oConfig);
4330
4331
4332     if (!this.lazyLoad) {
4333
4334         oLI = this.parent.element;
4335
4336         if (this.element.parentNode == oLI) {
4337     
4338             this.render();
4339     
4340         }
4341         else {
4342
4343             this.render(oLI);
4344     
4345         }
4346
4347     }
4348     
4349 },
4350
4351
4352 /**
4353 * @method _onMenuItemDestroy
4354 * @description "destroy" event handler for the menu's items.
4355 * @private
4356 * @param {String} p_sType String representing the name of the event 
4357 * that was fired.
4358 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4359 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
4360 * that fired the event.
4361 */
4362 _onMenuItemDestroy: function (p_sType, p_aArgs, p_oItem) {
4363
4364     this._removeItemFromGroupByValue(p_oItem.groupIndex, p_oItem);
4365
4366 },
4367
4368
4369 /**
4370 * @method _onMenuItemConfigChange
4371 * @description "configchange" event handler for the menu's items.
4372 * @private
4373 * @param {String} p_sType String representing the name of the event that 
4374 * was fired.
4375 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4376 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
4377 * that fired the event.
4378 */
4379 _onMenuItemConfigChange: function (p_sType, p_aArgs, p_oItem) {
4380
4381     var sPropertyName = p_aArgs[0][0],
4382         oPropertyValue = p_aArgs[0][1],
4383         oSubmenu;
4384
4385
4386     switch(sPropertyName) {
4387
4388         case _SELECTED:
4389
4390             if (oPropertyValue === true) {
4391
4392                 this.activeItem = p_oItem;
4393             
4394             }
4395
4396         break;
4397
4398         case _SUBMENU:
4399
4400             oSubmenu = p_aArgs[0][1];
4401
4402             if (oSubmenu) {
4403
4404                 this._configureSubmenu(p_oItem);
4405
4406             }
4407
4408         break;
4409
4410     }
4411
4412 },
4413
4414
4415
4416 // Public event handlers for configuration properties
4417
4418
4419 /**
4420 * @method configVisible
4421 * @description Event handler for when the "visible" configuration property 
4422 * the menu changes.
4423 * @param {String} p_sType String representing the name of the event that 
4424 * was fired.
4425 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4426 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4427 * fired the event.
4428 */
4429 configVisible: function (p_sType, p_aArgs, p_oMenu) {
4430
4431     var bVisible,
4432         sDisplay;
4433
4434     if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {
4435
4436         Menu.superclass.configVisible.call(this, p_sType, p_aArgs, p_oMenu);
4437
4438     }
4439     else {
4440
4441         bVisible = p_aArgs[0];
4442         sDisplay = Dom.getStyle(this.element, _DISPLAY);
4443
4444         Dom.setStyle(this.element, _VISIBILITY, _VISIBLE);
4445
4446         if (bVisible) {
4447
4448             if (sDisplay != _BLOCK) {
4449                 this.beforeShowEvent.fire();
4450                 Dom.setStyle(this.element, _DISPLAY, _BLOCK);
4451                 this.showEvent.fire();
4452             }
4453         
4454         }
4455         else {
4456
4457             if (sDisplay == _BLOCK) {
4458                 this.beforeHideEvent.fire();
4459                 Dom.setStyle(this.element, _DISPLAY, _NONE);
4460                 this.hideEvent.fire();
4461             }
4462         
4463         }
4464
4465     }
4466
4467 },
4468
4469
4470 /**
4471 * @method configPosition
4472 * @description Event handler for when the "position" configuration property 
4473 * of the menu changes.
4474 * @param {String} p_sType String representing the name of the event that 
4475 * was fired.
4476 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4477 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4478 * fired the event.
4479 */
4480 configPosition: function (p_sType, p_aArgs, p_oMenu) {
4481
4482     var oElement = this.element,
4483         sCSSPosition = p_aArgs[0] == _STATIC ? _STATIC : _ABSOLUTE,
4484         oCfg = this.cfg,
4485         nZIndex;
4486
4487
4488     Dom.setStyle(oElement, _POSITION, sCSSPosition);
4489
4490
4491     if (sCSSPosition == _STATIC) {
4492
4493         // Statically positioned menus are visible by default
4494         
4495         Dom.setStyle(oElement, _DISPLAY, _BLOCK);
4496
4497         oCfg.setProperty(_VISIBLE, true);
4498
4499     }
4500     else {
4501
4502         /*
4503             Even though the "visible" property is queued to 
4504             "false" by default, we need to set the "visibility" property to 
4505             "hidden" since Overlay's "configVisible" implementation checks the 
4506             element's "visibility" style property before deciding whether 
4507             or not to show an Overlay instance.
4508         */
4509
4510         Dom.setStyle(oElement, _VISIBILITY, _HIDDEN);
4511     
4512     }
4513
4514
4515      if (sCSSPosition == _ABSOLUTE) {
4516          nZIndex = oCfg.getProperty(_ZINDEX);
4517
4518          if (!nZIndex || nZIndex === 0) {
4519              oCfg.setProperty(_ZINDEX, 1);
4520          }
4521
4522      }
4523
4524 },
4525
4526
4527 /**
4528 * @method configIframe
4529 * @description Event handler for when the "iframe" configuration property of 
4530 * the menu changes.
4531 * @param {String} p_sType String representing the name of the event that 
4532 * was fired.
4533 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4534 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4535 * fired the event.
4536 */
4537 configIframe: function (p_sType, p_aArgs, p_oMenu) {    
4538
4539     if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {
4540
4541         Menu.superclass.configIframe.call(this, p_sType, p_aArgs, p_oMenu);
4542
4543     }
4544
4545 },
4546
4547
4548 /**
4549 * @method configHideDelay
4550 * @description Event handler for when the "hidedelay" configuration property 
4551 * of the menu changes.
4552 * @param {String} p_sType String representing the name of the event that 
4553 * was fired.
4554 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4555 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4556 * fired the event.
4557 */
4558 configHideDelay: function (p_sType, p_aArgs, p_oMenu) {
4559
4560     var nHideDelay = p_aArgs[0];
4561
4562     this._useHideDelay = (nHideDelay > 0);
4563
4564 },
4565
4566
4567 /**
4568 * @method configContainer
4569 * @description Event handler for when the "container" configuration property 
4570 * of the menu changes.
4571 * @param {String} p_sType String representing the name of the event that 
4572 * was fired.
4573 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4574 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
4575 * fired the event.
4576 */
4577 configContainer: function (p_sType, p_aArgs, p_oMenu) {
4578
4579     var oElement = p_aArgs[0];
4580
4581     if (Lang.isString(oElement)) {
4582
4583         this.cfg.setProperty(_CONTAINER, Dom.get(oElement), true);
4584
4585     }
4586
4587 },
4588
4589
4590 /**
4591 * @method _clearSetWidthFlag
4592 * @description Change event listener for the "width" configuration property.  This listener is 
4593 * added when a Menu's "width" configuration property is set by the "_setScrollHeight" method, and 
4594 * is used to set the "_widthSetForScroll" property to "false" if the "width" configuration property 
4595 * is changed after it was set by the "_setScrollHeight" method.  If the "_widthSetForScroll" 
4596 * property is set to "false", and the "_setScrollHeight" method is in the process of tearing down 
4597 * scrolling functionality, it will maintain the Menu's new width rather than reseting it.
4598 * @private
4599 */
4600 _clearSetWidthFlag: function () {
4601
4602     this._widthSetForScroll = false;
4603     
4604     this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
4605
4606 },
4607
4608 /**
4609  * @method _subscribeScrollHandlers
4610  * @param {HTMLElement} oHeader The scroll header element
4611  * @param {HTMLElement} oFooter The scroll footer element
4612  */
4613 _subscribeScrollHandlers : function(oHeader, oFooter) {
4614     var fnMouseOver = this._onScrollTargetMouseOver;
4615     var fnMouseOut = this._onScrollTargetMouseOut;
4616
4617     Event.on(oHeader, _MOUSEOVER, fnMouseOver, this, true);
4618     Event.on(oHeader, _MOUSEOUT, fnMouseOut, this, true);
4619     Event.on(oFooter, _MOUSEOVER, fnMouseOver, this, true);
4620     Event.on(oFooter, _MOUSEOUT, fnMouseOut, this, true);
4621 },
4622
4623 /**
4624  * @method _unsubscribeScrollHandlers 
4625  * @param {HTMLElement} oHeader The scroll header element
4626  * @param {HTMLElement} oFooter The scroll footer element
4627  */
4628 _unsubscribeScrollHandlers : function(oHeader, oFooter) {
4629     var fnMouseOver = this._onScrollTargetMouseOver;
4630     var fnMouseOut = this._onScrollTargetMouseOut;
4631     
4632     Event.removeListener(oHeader, _MOUSEOVER, fnMouseOver);
4633     Event.removeListener(oHeader, _MOUSEOUT, fnMouseOut);
4634     Event.removeListener(oFooter, _MOUSEOVER, fnMouseOver);
4635     Event.removeListener(oFooter, _MOUSEOUT, fnMouseOut);
4636 },
4637
4638 /**
4639 * @method _setScrollHeight
4640 * @description 
4641 * @param {String} p_nScrollHeight Number representing the scrolling height of the Menu.
4642 * @private
4643 */
4644 _setScrollHeight: function (p_nScrollHeight) {
4645
4646     var nScrollHeight = p_nScrollHeight,
4647         bRefireIFrameAndShadow = false,
4648         bSetWidth = false,
4649         oElement,
4650         oBody,
4651         oHeader,
4652         oFooter,
4653         nMinScrollHeight,
4654         nHeight,
4655         nOffsetWidth,
4656         sWidth;
4657
4658     if (this.getItems().length > 0) {
4659
4660         oElement = this.element;
4661         oBody = this.body;
4662         oHeader = this.header;
4663         oFooter = this.footer;
4664         nMinScrollHeight = this.cfg.getProperty(_MIN_SCROLL_HEIGHT);
4665
4666         if (nScrollHeight > 0 && nScrollHeight < nMinScrollHeight) {
4667             nScrollHeight = nMinScrollHeight;
4668         }
4669
4670         Dom.setStyle(oBody, _HEIGHT, _EMPTY_STRING);
4671         Dom.removeClass(oBody, _YUI_MENU_BODY_SCROLLED);
4672         oBody.scrollTop = 0;
4673
4674         //      Need to set a width for the Menu to fix the following problems in 
4675         //      Firefox 2 and IE:
4676
4677         //      #1) Scrolled Menus will render at 1px wide in Firefox 2
4678
4679         //      #2) There is a bug in gecko-based browsers where an element whose 
4680         //      "position" property is set to "absolute" and "overflow" property is 
4681         //      set to "hidden" will not render at the correct width when its 
4682         //      offsetParent's "position" property is also set to "absolute."  It is 
4683         //      possible to work around this bug by specifying a value for the width 
4684         //      property in addition to overflow.
4685
4686         //      #3) In IE it is necessary to give the Menu a width before the 
4687         //      scrollbars are rendered to prevent the Menu from rendering with a 
4688         //      width that is 100% of the browser viewport.
4689
4690         bSetWidth = ((UA.gecko && UA.gecko < 1.9) || UA.ie);
4691
4692         if (nScrollHeight > 0 && bSetWidth && !this.cfg.getProperty(_WIDTH)) {
4693
4694             nOffsetWidth = oElement.offsetWidth;
4695     
4696             /*
4697                 Measuring the difference of the offsetWidth before and after
4698                 setting the "width" style attribute allows us to compute the 
4699                 about of padding and borders applied to the element, which in 
4700                 turn allows us to set the "width" property correctly.
4701             */
4702             
4703             oElement.style.width = nOffsetWidth + _PX;
4704     
4705             sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX;
4706
4707
4708             this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
4709
4710
4711             this.cfg.setProperty(_WIDTH, sWidth);
4712
4713
4714             /*
4715                 Set a flag (_widthSetForScroll) to maintain some history regarding how the 
4716                 "width" configuration property was set.  If the "width" configuration property 
4717                 is set by something other than the "_setScrollHeight" method, it will be 
4718                 necessary to maintain that new value and not clear the width if scrolling 
4719                 is turned off.
4720             */
4721
4722             this._widthSetForScroll = true;
4723
4724             this.cfg.subscribeToConfigEvent(_WIDTH, this._clearSetWidthFlag);
4725     
4726         }
4727
4728
4729         if (nScrollHeight > 0 && (!oHeader && !oFooter)) {
4730
4731
4732             this.setHeader(_NON_BREAKING_SPACE);
4733             this.setFooter(_NON_BREAKING_SPACE);
4734
4735             oHeader = this.header;
4736             oFooter = this.footer;
4737
4738             Dom.addClass(oHeader, _TOP_SCROLLBAR);
4739             Dom.addClass(oFooter, _BOTTOM_SCROLLBAR);
4740
4741             oElement.insertBefore(oHeader, oBody);
4742             oElement.appendChild(oFooter);
4743         
4744         }
4745
4746         nHeight = nScrollHeight;
4747
4748         if (oHeader && oFooter) {
4749             nHeight = (nHeight - (oHeader.offsetHeight + oFooter.offsetHeight));
4750         }
4751     
4752     
4753         if ((nHeight > 0) && (oBody.offsetHeight > nScrollHeight)) {
4754
4755     
4756             Dom.addClass(oBody, _YUI_MENU_BODY_SCROLLED);
4757             Dom.setStyle(oBody, _HEIGHT, (nHeight + _PX));
4758
4759             if (!this._hasScrollEventHandlers) {
4760                 this._subscribeScrollHandlers(oHeader, oFooter);
4761                 this._hasScrollEventHandlers = true;
4762             }
4763     
4764             this._disableScrollHeader();
4765             this._enableScrollFooter();
4766             
4767             bRefireIFrameAndShadow = true;                      
4768     
4769         }
4770         else if (oHeader && oFooter) {
4771
4772     
4773
4774             /*
4775                 Only clear the the "width" configuration property if it was set the 
4776                 "_setScrollHeight" method and wasn't changed by some other means after it was set.
4777             */  
4778     
4779             if (this._widthSetForScroll) {
4780     
4781
4782                 this._widthSetForScroll = false;
4783
4784                 this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
4785     
4786                 this.cfg.setProperty(_WIDTH, _EMPTY_STRING);
4787             
4788             }
4789     
4790     
4791             this._enableScrollHeader();
4792             this._enableScrollFooter();
4793     
4794             if (this._hasScrollEventHandlers) {
4795                 this._unsubscribeScrollHandlers(oHeader, oFooter);    
4796                 this._hasScrollEventHandlers = false;
4797             }
4798
4799             oElement.removeChild(oHeader);
4800             oElement.removeChild(oFooter);
4801     
4802             this.header = null;
4803             this.footer = null;
4804             
4805             bRefireIFrameAndShadow = true;
4806         
4807         }
4808
4809
4810         if (bRefireIFrameAndShadow) {
4811     
4812             this.cfg.refireEvent(_IFRAME);
4813             this.cfg.refireEvent(_SHADOW);
4814         
4815         }
4816     
4817     }
4818
4819 },
4820
4821
4822 /**
4823 * @method _setMaxHeight
4824 * @description "renderEvent" handler used to defer the setting of the 
4825 * "maxheight" configuration property until the menu is rendered in lazy 
4826 * load scenarios.
4827 * @param {String} p_sType The name of the event that was fired.
4828 * @param {Array} p_aArgs Collection of arguments sent when the event 
4829 * was fired.
4830 * @param {Number} p_nMaxHeight Number representing the value to set for the 
4831 * "maxheight" configuration property.
4832 * @private
4833 */
4834 _setMaxHeight: function (p_sType, p_aArgs, p_nMaxHeight) {
4835
4836     this._setScrollHeight(p_nMaxHeight);
4837     this.renderEvent.unsubscribe(this._setMaxHeight);
4838
4839 },
4840
4841
4842 /**
4843 * @method configMaxHeight
4844 * @description Event handler for when the "maxheight" configuration property of 
4845 * a Menu changes.
4846 * @param {String} p_sType The name of the event that was fired.
4847 * @param {Array} p_aArgs Collection of arguments sent when the event 
4848 * was fired.
4849 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired
4850 * the event.
4851 */
4852 configMaxHeight: function (p_sType, p_aArgs, p_oMenu) {
4853
4854     var nMaxHeight = p_aArgs[0];
4855
4856     if (this.lazyLoad && !this.body && nMaxHeight > 0) {
4857     
4858         this.renderEvent.subscribe(this._setMaxHeight, nMaxHeight, this);
4859
4860     }
4861     else {
4862
4863         this._setScrollHeight(nMaxHeight);
4864     
4865     }
4866
4867 },
4868
4869
4870 /**
4871 * @method configClassName
4872 * @description Event handler for when the "classname" configuration property of 
4873 * a menu changes.
4874 * @param {String} p_sType The name of the event that was fired.
4875 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
4876 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
4877 */
4878 configClassName: function (p_sType, p_aArgs, p_oMenu) {
4879
4880     var sClassName = p_aArgs[0];
4881
4882     if (this._sClassName) {
4883
4884         Dom.removeClass(this.element, this._sClassName);
4885
4886     }
4887
4888     Dom.addClass(this.element, sClassName);
4889     this._sClassName = sClassName;
4890
4891 },
4892
4893
4894 /**
4895 * @method _onItemAdded
4896 * @description "itemadded" event handler for a Menu instance.
4897 * @private
4898 * @param {String} p_sType The name of the event that was fired.
4899 * @param {Array} p_aArgs Collection of arguments sent when the event 
4900 * was fired.
4901 */
4902 _onItemAdded: function (p_sType, p_aArgs) {
4903
4904     var oItem = p_aArgs[0];
4905     
4906     if (oItem) {
4907
4908         oItem.cfg.setProperty(_DISABLED, true);
4909     
4910     }
4911
4912 },
4913
4914
4915 /**
4916 * @method configDisabled
4917 * @description Event handler for when the "disabled" configuration property of 
4918 * a menu changes.
4919 * @param {String} p_sType The name of the event that was fired.
4920 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
4921 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
4922 */
4923 configDisabled: function (p_sType, p_aArgs, p_oMenu) {
4924
4925     var bDisabled = p_aArgs[0],
4926         aItems = this.getItems(),
4927         nItems,
4928         i;
4929
4930     if (Lang.isArray(aItems)) {
4931
4932         nItems = aItems.length;
4933     
4934         if (nItems > 0) {
4935         
4936             i = nItems - 1;
4937     
4938             do {
4939     
4940                 aItems[i].cfg.setProperty(_DISABLED, bDisabled);
4941             
4942             }
4943             while (i--);
4944         
4945         }
4946
4947
4948         if (bDisabled) {
4949
4950             this.clearActiveItem(true);
4951
4952             Dom.addClass(this.element, _DISABLED);
4953
4954             this.itemAddedEvent.subscribe(this._onItemAdded);
4955
4956         }
4957         else {
4958
4959             Dom.removeClass(this.element, _DISABLED);
4960
4961             this.itemAddedEvent.unsubscribe(this._onItemAdded);
4962
4963         }
4964         
4965     }
4966
4967 },
4968
4969 /**
4970  * Resizes the shadow to match the container bounding element
4971  * 
4972  * @method _sizeShadow
4973  * @protected
4974  */
4975 _sizeShadow : function () {
4976
4977     var oElement = this.element,
4978         oShadow = this._shadow;
4979
4980     if (oShadow && oElement) {
4981         // Clear the previous width
4982         if (oShadow.style.width && oShadow.style.height) {
4983             oShadow.style.width = _EMPTY_STRING;
4984             oShadow.style.height = _EMPTY_STRING;
4985         }
4986
4987         oShadow.style.width = (oElement.offsetWidth + 6) + _PX;
4988         oShadow.style.height = (oElement.offsetHeight + 1) + _PX;
4989     }
4990 },
4991
4992 /**
4993  * Replaces the shadow element in the DOM with the current shadow element (this._shadow)
4994  * 
4995  * @method _replaceShadow
4996  * @protected 
4997  */
4998 _replaceShadow : function () {
4999     this.element.appendChild(this._shadow);
5000 },
5001
5002 /**
5003  * Adds the classname marker for a visible shadow, to the shadow element
5004  * 
5005  * @method _addShadowVisibleClass
5006  * @protected
5007  */
5008 _addShadowVisibleClass : function () {
5009     Dom.addClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE);
5010 },
5011
5012 /**
5013  * Removes the classname marker for a visible shadow, from the shadow element
5014  * 
5015  * @method _removeShadowVisibleClass
5016  * @protected
5017  */
5018 _removeShadowVisibleClass : function () {
5019     Dom.removeClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE);
5020 },
5021
5022 /**
5023  * Removes the shadow element from the DOM, and unsubscribes all the listeners used to keep it in sync. Used
5024  * to handle setting the shadow to false.
5025  * 
5026  * @method _removeShadow
5027  * @protected
5028  */
5029 _removeShadow : function() {
5030
5031     var p = (this._shadow && this._shadow.parentNode);
5032
5033     if (p) {
5034         p.removeChild(this._shadow);
5035     }
5036
5037     this.beforeShowEvent.unsubscribe(this._addShadowVisibleClass);
5038     this.beforeHideEvent.unsubscribe(this._removeShadowVisibleClass);
5039
5040     this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._sizeShadow);
5041     this.cfg.unsubscribeFromConfigEvent(_HEIGHT, this._sizeShadow);
5042     this.cfg.unsubscribeFromConfigEvent(_MAX_HEIGHT, this._sizeShadow);
5043     this.cfg.unsubscribeFromConfigEvent(_MAX_HEIGHT, this._replaceShadow);
5044
5045     this.changeContentEvent.unsubscribe(this._sizeShadow);
5046
5047     Module.textResizeEvent.unsubscribe(this._sizeShadow);
5048 },
5049
5050 /**
5051  * Used to create the shadow element, add it to the DOM, and subscribe listeners to keep it in sync.
5052  *
5053  * @method _createShadow
5054  * @protected
5055  */
5056 _createShadow : function () {
5057
5058     var oShadow = this._shadow,
5059         oElement;
5060
5061     if (!oShadow) {
5062         oElement = this.element;
5063
5064         if (!m_oShadowTemplate) {
5065             m_oShadowTemplate = document.createElement(_DIV_LOWERCASE);
5066             m_oShadowTemplate.className = _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE;
5067         }
5068
5069         oShadow = m_oShadowTemplate.cloneNode(false);
5070
5071         oElement.appendChild(oShadow);
5072         
5073         this._shadow = oShadow;
5074
5075         this.beforeShowEvent.subscribe(this._addShadowVisibleClass);
5076         this.beforeHideEvent.subscribe(this._removeShadowVisibleClass);
5077
5078         if (UA.ie) {
5079             /*
5080                  Need to call sizeShadow & syncIframe via setTimeout for 
5081                  IE 7 Quirks Mode and IE 6 Standards Mode and Quirks Mode 
5082                  or the shadow and iframe shim will not be sized and 
5083                  positioned properly.
5084             */
5085             Lang.later(0, this, function () {
5086                 this._sizeShadow(); 
5087                 this.syncIframe();
5088             });
5089
5090             this.cfg.subscribeToConfigEvent(_WIDTH, this._sizeShadow);
5091             this.cfg.subscribeToConfigEvent(_HEIGHT, this._sizeShadow);
5092             this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, this._sizeShadow);
5093             this.changeContentEvent.subscribe(this._sizeShadow);
5094
5095             Module.textResizeEvent.subscribe(this._sizeShadow, this, true);
5096
5097             this.destroyEvent.subscribe(function () {
5098                 Module.textResizeEvent.unsubscribe(this._sizeShadow, this);
5099             });
5100         }
5101
5102         this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, this._replaceShadow);
5103     }
5104 },
5105
5106 /**
5107  * The beforeShow event handler used to set up the shadow lazily when the menu is made visible.
5108  * @method _shadowBeforeShow
5109  * @protected 
5110  */
5111 _shadowBeforeShow : function () {
5112     if (this._shadow) {
5113
5114         // If called because the "shadow" event was refired - just append again and resize
5115         this._replaceShadow();
5116
5117         if (UA.ie) {
5118             this._sizeShadow();
5119         }
5120     } else {
5121         this._createShadow();
5122     }
5123
5124     this.beforeShowEvent.unsubscribe(this._shadowBeforeShow);
5125 },
5126
5127 /**
5128 * @method configShadow
5129 * @description Event handler for when the "shadow" configuration property of 
5130 * a menu changes.
5131 * @param {String} p_sType The name of the event that was fired.
5132 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
5133 * @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
5134 */
5135 configShadow: function (p_sType, p_aArgs, p_oMenu) {
5136
5137     var bShadow = p_aArgs[0];
5138
5139     if (bShadow && this.cfg.getProperty(_POSITION) == _DYNAMIC) {
5140         if (this.cfg.getProperty(_VISIBLE)) {
5141             if (this._shadow) {
5142                 // If the "shadow" event was refired - just append again and resize
5143                 this._replaceShadow();
5144                 
5145                 if (UA.ie) {
5146                     this._sizeShadow();
5147                 }
5148             } else {
5149                 this._createShadow();
5150             }
5151         } else {
5152             this.beforeShowEvent.subscribe(this._shadowBeforeShow);
5153         }
5154     } else if (!bShadow) {
5155         this.beforeShowEvent.unsubscribe(this._shadowBeforeShow);
5156         this._removeShadow();
5157     }
5158 },
5159
5160 // Public methods
5161
5162 /**
5163 * @method initEvents
5164 * @description Initializes the custom events for the menu.
5165 */
5166 initEvents: function () {
5167
5168     Menu.superclass.initEvents.call(this);
5169
5170     // Create custom events
5171
5172     var i = EVENT_TYPES.length - 1,
5173         aEventData,
5174         oCustomEvent;
5175
5176
5177     do {
5178
5179         aEventData = EVENT_TYPES[i];
5180
5181         oCustomEvent = this.createEvent(aEventData[1]);
5182         oCustomEvent.signature = CustomEvent.LIST;
5183         
5184         this[aEventData[0]] = oCustomEvent;
5185
5186     }
5187     while (i--);
5188
5189 },
5190
5191
5192 /**
5193 * @method positionOffScreen
5194 * @description Positions the menu outside of the boundaries of the browser's 
5195 * viewport.  Called automatically when a menu is hidden to ensure that 
5196 * it doesn't force the browser to render uncessary scrollbars.
5197 */
5198 positionOffScreen: function () {
5199
5200     var oIFrame = this.iframe,
5201         oElement = this.element,
5202         sPos = this.OFF_SCREEN_POSITION;
5203     
5204     oElement.style.top = _EMPTY_STRING;
5205     oElement.style.left = _EMPTY_STRING;
5206     
5207     if (oIFrame) {
5208
5209         oIFrame.style.top = sPos;
5210         oIFrame.style.left = sPos;
5211     
5212     }
5213
5214 },
5215
5216
5217 /**
5218 * @method getRoot
5219 * @description Finds the menu's root menu.
5220 */
5221 getRoot: function () {
5222
5223     var oItem = this.parent,
5224         oParentMenu,
5225         returnVal;
5226
5227     if (oItem) {
5228
5229         oParentMenu = oItem.parent;
5230
5231         returnVal = oParentMenu ? oParentMenu.getRoot() : this;
5232
5233     }
5234     else {
5235     
5236         returnVal = this;
5237     
5238     }
5239     
5240     return returnVal;
5241
5242 },
5243
5244
5245 /**
5246 * @method toString
5247 * @description Returns a string representing the menu.
5248 * @return {String}
5249 */
5250 toString: function () {
5251
5252     var sReturnVal = _MENU,
5253         sId = this.id;
5254
5255     if (sId) {
5256
5257         sReturnVal += (_SPACE + sId);
5258     
5259     }
5260
5261     return sReturnVal;
5262
5263 },
5264
5265
5266 /**
5267 * @method setItemGroupTitle
5268 * @description Sets the title of a group of menu items.
5269 * @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.
5270 * @param {Number} p_nGroupIndex Optional. Number specifying the group to which
5271 * the title belongs.
5272 */
5273 setItemGroupTitle: function (p_sGroupTitle, p_nGroupIndex) {
5274
5275     var nGroupIndex,
5276         oTitle,
5277         i,
5278         nFirstIndex;
5279         
5280     if (Lang.isString(p_sGroupTitle) && p_sGroupTitle.length > 0) {
5281
5282         nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0;
5283         oTitle = this._aGroupTitleElements[nGroupIndex];
5284
5285
5286         if (oTitle) {
5287
5288             oTitle.innerHTML = p_sGroupTitle;
5289             
5290         }
5291         else {
5292
5293             oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME);
5294                     
5295             oTitle.innerHTML = p_sGroupTitle;
5296
5297             this._aGroupTitleElements[nGroupIndex] = oTitle;
5298
5299         }
5300
5301
5302         i = this._aGroupTitleElements.length - 1;
5303
5304         do {
5305
5306             if (this._aGroupTitleElements[i]) {
5307
5308                 Dom.removeClass(this._aGroupTitleElements[i], _FIRST_OF_TYPE);
5309
5310                 nFirstIndex = i;
5311
5312             }
5313
5314         }
5315         while (i--);
5316
5317
5318         if (nFirstIndex !== null) {
5319
5320             Dom.addClass(this._aGroupTitleElements[nFirstIndex], 
5321                 _FIRST_OF_TYPE);
5322
5323         }
5324
5325         this.changeContentEvent.fire();
5326
5327     }
5328
5329 },
5330
5331
5332
5333 /**
5334 * @method addItem
5335 * @description Appends an item to the menu.
5336 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
5337 * instance to be added to the menu.
5338 * @param {HTML} p_oItem String or markup specifying content of the item to be added 
5339 * 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.
5340 * @param {Object} p_oItem Object literal containing a set of menu item 
5341 * configuration properties.
5342 * @param {Number} p_nGroupIndex Optional. Number indicating the group to
5343 * which the item belongs.
5344 * @return {YAHOO.widget.MenuItem}
5345 */
5346 addItem: function (p_oItem, p_nGroupIndex) {
5347
5348     return this._addItemToGroup(p_nGroupIndex, p_oItem);
5349
5350 },
5351
5352
5353 /**
5354 * @method addItems
5355 * @description Adds an array of items to the menu.
5356 * @param {Array} p_aItems Array of items to be added to the menu.  The array 
5357 * can contain strings specifying the markup for the content of each item to be created, object
5358 * literals specifying each of the menu item configuration properties, 
5359 * 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.
5360 * @param {Number} p_nGroupIndex Optional. Number specifying the group to 
5361 * which the items belongs.
5362 * @return {Array}
5363 */
5364 addItems: function (p_aItems, p_nGroupIndex) {
5365
5366     var nItems,
5367         aItems,
5368         oItem,
5369         i,
5370         returnVal;
5371
5372
5373     if (Lang.isArray(p_aItems)) {
5374
5375         nItems = p_aItems.length;
5376         aItems = [];
5377
5378         for(i=0; i<nItems; i++) {
5379
5380             oItem = p_aItems[i];
5381
5382             if (oItem) {
5383
5384                 if (Lang.isArray(oItem)) {
5385     
5386                     aItems[aItems.length] = this.addItems(oItem, i);
5387     
5388                 }
5389                 else {
5390     
5391                     aItems[aItems.length] = this._addItemToGroup(p_nGroupIndex, oItem);
5392                 
5393                 }
5394
5395             }
5396     
5397         }
5398
5399
5400         if (aItems.length) {
5401         
5402             returnVal = aItems;
5403         
5404         }
5405
5406     }
5407
5408     return returnVal;
5409
5410 },
5411
5412
5413 /**
5414 * @method insertItem
5415 * @description Inserts an item into the menu at the specified index.
5416 * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
5417 * instance to be added to the menu.
5418 * @param {String} p_oItem String specifying the text of the item to be added 
5419 * to the menu.
5420 * @param {Object} p_oItem Object literal containing a set of menu item 
5421 * configuration properties.
5422 * @param {Number} p_nItemIndex Number indicating the ordinal position at which
5423 * the item should be added.
5424 * @param {Number} p_nGroupIndex Optional. Number indicating the group to which 
5425 * the item belongs.
5426 * @return {YAHOO.widget.MenuItem}
5427 */
5428 insertItem: function (p_oItem, p_nItemIndex, p_nGroupIndex) {
5429     
5430     return this._addItemToGroup(p_nGroupIndex, p_oItem, p_nItemIndex);
5431
5432 },
5433
5434
5435 /**
5436 * @method removeItem
5437 * @description Removes the specified item from the menu.
5438 * @param {YAHOO.widget.MenuItem} p_oObject Object reference for the MenuItem 
5439 * instance to be removed from the menu.
5440 * @param {Number} p_oObject Number specifying the index of the item 
5441 * to be removed.
5442 * @param {Number} p_nGroupIndex Optional. Number specifying the group to 
5443 * which the item belongs.
5444 * @return {YAHOO.widget.MenuItem}
5445 */
5446 removeItem: function (p_oObject, p_nGroupIndex) {
5447
5448     var oItem,
5449         returnVal;
5450     
5451     if (!Lang.isUndefined(p_oObject)) {
5452
5453         if (p_oObject instanceof YAHOO.widget.MenuItem) {
5454
5455             oItem = this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject);           
5456
5457         }
5458         else if (Lang.isNumber(p_oObject)) {
5459
5460             oItem = this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject);
5461
5462         }
5463
5464         if (oItem) {
5465
5466             oItem.destroy();
5467
5468
5469             returnVal = oItem;
5470
5471         }
5472
5473     }
5474
5475     return returnVal;
5476
5477 },
5478
5479
5480 /**
5481 * @method getItems
5482 * @description Returns an array of all of the items in the menu.
5483 * @return {Array}
5484 */
5485 getItems: function () {
5486
5487     var aGroups = this._aItemGroups,
5488         nGroups,
5489         returnVal,
5490         aItems = [];
5491
5492
5493     if (Lang.isArray(aGroups)) {
5494
5495         nGroups = aGroups.length;
5496
5497         returnVal = ((nGroups == 1) ? aGroups[0] : (Array.prototype.concat.apply(aItems, aGroups)));
5498
5499     }
5500
5501     return returnVal;
5502
5503 },
5504
5505
5506 /**
5507 * @method getItemGroups
5508 * @description Multi-dimensional Array representing the menu items as they 
5509 * are grouped in the menu.
5510 * @return {Array}
5511 */        
5512 getItemGroups: function () {
5513
5514     return this._aItemGroups;
5515
5516 },
5517
5518
5519 /**
5520 * @method getItem
5521 * @description Returns the item at the specified index.
5522 * @param {Number} p_nItemIndex Number indicating the ordinal position of the 
5523 * item to be retrieved.
5524 * @param {Number} p_nGroupIndex Optional. Number indicating the group to which 
5525 * the item belongs.
5526 * @return {YAHOO.widget.MenuItem}
5527 */
5528 getItem: function (p_nItemIndex, p_nGroupIndex) {
5529     
5530     var aGroup,
5531         returnVal;
5532     
5533     if (Lang.isNumber(p_nItemIndex)) {
5534
5535         aGroup = this._getItemGroup(p_nGroupIndex);
5536
5537         if (aGroup) {
5538
5539             returnVal = aGroup[p_nItemIndex];
5540         
5541         }
5542
5543     }
5544     
5545     return returnVal;
5546     
5547 },
5548
5549
5550 /**
5551 * @method getSubmenus
5552 * @description Returns an array of all of the submenus that are immediate 
5553 * children of the menu.
5554 * @return {Array}
5555 */
5556 getSubmenus: function () {
5557
5558     var aItems = this.getItems(),
5559         nItems = aItems.length,
5560         aSubmenus,
5561         oSubmenu,
5562         oItem,
5563         i;
5564
5565
5566     if (nItems > 0) {
5567         
5568         aSubmenus = [];
5569
5570         for(i=0; i<nItems; i++) {
5571
5572             oItem = aItems[i];
5573             
5574             if (oItem) {
5575
5576                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5577                 
5578                 if (oSubmenu) {
5579
5580                     aSubmenus[aSubmenus.length] = oSubmenu;
5581
5582                 }
5583             
5584             }
5585         
5586         }
5587     
5588     }
5589
5590     return aSubmenus;
5591
5592 },
5593
5594
5595 /**
5596 * @method clearContent
5597 * @description Removes all of the content from the menu, including the menu 
5598 * items, group titles, header and footer.
5599 */
5600 clearContent: function () {
5601
5602     var aItems = this.getItems(),
5603         nItems = aItems.length,
5604         oElement = this.element,
5605         oBody = this.body,
5606         oHeader = this.header,
5607         oFooter = this.footer,
5608         oItem,
5609         oSubmenu,
5610         i;
5611
5612
5613     if (nItems > 0) {
5614
5615         i = nItems - 1;
5616
5617         do {
5618
5619             oItem = aItems[i];
5620
5621             if (oItem) {
5622
5623                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5624
5625                 if (oSubmenu) {
5626
5627                     this.cfg.configChangedEvent.unsubscribe(
5628                         this._onParentMenuConfigChange, oSubmenu);
5629
5630                     this.renderEvent.unsubscribe(this._onParentMenuRender, 
5631                         oSubmenu);
5632
5633                 }
5634                 
5635                 this.removeItem(oItem, oItem.groupIndex);
5636
5637             }
5638         
5639         }
5640         while (i--);
5641
5642     }
5643
5644
5645     if (oHeader) {
5646
5647         Event.purgeElement(oHeader);
5648         oElement.removeChild(oHeader);
5649
5650     }
5651     
5652
5653     if (oFooter) {
5654
5655         Event.purgeElement(oFooter);
5656         oElement.removeChild(oFooter);
5657     }
5658
5659
5660     if (oBody) {
5661
5662         Event.purgeElement(oBody);
5663
5664         oBody.innerHTML = _EMPTY_STRING;
5665
5666     }
5667
5668     this.activeItem = null;
5669
5670     this._aItemGroups = [];
5671     this._aListElements = [];
5672     this._aGroupTitleElements = [];
5673
5674     this.cfg.setProperty(_WIDTH, null);
5675
5676 },
5677
5678
5679 /**
5680 * @method destroy
5681 * @description Removes the menu's <code>&#60;div&#62;</code> element 
5682 * (and accompanying child nodes) from the document.
5683 * @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. 
5684 * 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.
5685
5686 */
5687 destroy: function (shallowPurge) {
5688
5689     // Remove all items
5690
5691     this.clearContent();
5692
5693     this._aItemGroups = null;
5694     this._aListElements = null;
5695     this._aGroupTitleElements = null;
5696
5697
5698     // Continue with the superclass implementation of this method
5699
5700     Menu.superclass.destroy.call(this, shallowPurge);
5701     
5702
5703 },
5704
5705
5706 /**
5707 * @method setInitialFocus
5708 * @description Sets focus to the menu's first enabled item.
5709 */
5710 setInitialFocus: function () {
5711
5712     var oItem = this._getFirstEnabledItem();
5713     
5714     if (oItem) {
5715
5716         oItem.focus();
5717
5718     }
5719     
5720 },
5721
5722
5723 /**
5724 * @method setInitialSelection
5725 * @description Sets the "selected" configuration property of the menu's first 
5726 * enabled item to "true."
5727 */
5728 setInitialSelection: function () {
5729
5730     var oItem = this._getFirstEnabledItem();
5731     
5732     if (oItem) {
5733     
5734         oItem.cfg.setProperty(_SELECTED, true);
5735     }        
5736
5737 },
5738
5739
5740 /**
5741 * @method clearActiveItem
5742 * @description Sets the "selected" configuration property of the menu's active
5743 * item to "false" and hides the item's submenu.
5744 * @param {Boolean} p_bBlur Boolean indicating if the menu's active item 
5745 * should be blurred.  
5746 */
5747 clearActiveItem: function (p_bBlur) {
5748
5749     if (this.cfg.getProperty(_SHOW_DELAY) > 0) {
5750     
5751         this._cancelShowDelay();
5752     
5753     }
5754
5755
5756     var oActiveItem = this.activeItem,
5757         oConfig,
5758         oSubmenu;
5759
5760     if (oActiveItem) {
5761
5762         oConfig = oActiveItem.cfg;
5763
5764         if (p_bBlur) {
5765
5766             oActiveItem.blur();
5767             
5768             this.getRoot()._hasFocus = true;
5769         
5770         }
5771
5772         oConfig.setProperty(_SELECTED, false);
5773
5774         oSubmenu = oConfig.getProperty(_SUBMENU);
5775
5776
5777         if (oSubmenu) {
5778
5779             oSubmenu.hide();
5780
5781         }
5782
5783         this.activeItem = null;  
5784
5785     }
5786
5787 },
5788
5789
5790 /**
5791 * @method focus
5792 * @description Causes the menu to receive focus and fires the "focus" event.
5793 */
5794 focus: function () {
5795
5796     if (!this.hasFocus()) {
5797
5798         this.setInitialFocus();
5799     
5800     }
5801
5802 },
5803
5804
5805 /**
5806 * @method blur
5807 * @description Causes the menu to lose focus and fires the "blur" event.
5808 */    
5809 blur: function () {
5810
5811     var oItem;
5812
5813     if (this.hasFocus()) {
5814     
5815         oItem = MenuManager.getFocusedMenuItem();
5816         
5817         if (oItem) {
5818
5819             oItem.blur();
5820
5821         }
5822
5823     }
5824
5825 },
5826
5827
5828 /**
5829 * @method hasFocus
5830 * @description Returns a boolean indicating whether or not the menu has focus.
5831 * @return {Boolean}
5832 */
5833 hasFocus: function () {
5834
5835     return (MenuManager.getFocusedMenu() == this.getRoot());
5836
5837 },
5838
5839
5840 _doItemSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) {
5841
5842     var oItem = p_aArgs[0],
5843         oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5844
5845     if (oSubmenu) {
5846         oSubmenu.subscribe.apply(oSubmenu, p_oObject);
5847     }
5848
5849 },
5850
5851
5852 _doSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) { 
5853
5854     var oSubmenu = this.cfg.getProperty(_SUBMENU);
5855     
5856     if (oSubmenu) {
5857         oSubmenu.subscribe.apply(oSubmenu, p_oObject);
5858     }
5859
5860 },
5861
5862
5863 /**
5864 * Adds the specified CustomEvent subscriber to the menu and each of 
5865 * its submenus.
5866 * @method subscribe
5867 * @param p_type     {string}   the type, or name of the event
5868 * @param p_fn       {function} the function to exectute when the event fires
5869 * @param p_obj      {Object}   An object to be passed along when the event 
5870 *                              fires
5871 * @param p_override {boolean}  If true, the obj passed in becomes the 
5872 *                              execution scope of the listener
5873 */
5874 subscribe: function () {
5875
5876     //  Subscribe to the event for this Menu instance
5877     Menu.superclass.subscribe.apply(this, arguments);
5878
5879     //  Subscribe to the "itemAdded" event so that all future submenus
5880     //  also subscribe to this event
5881     Menu.superclass.subscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments);
5882
5883
5884     var aItems = this.getItems(),
5885         nItems,
5886         oItem,
5887         oSubmenu,
5888         i;
5889         
5890
5891     if (aItems) {
5892
5893         nItems = aItems.length;
5894         
5895         if (nItems > 0) {
5896         
5897             i = nItems - 1;
5898             
5899             do {
5900
5901                 oItem = aItems[i];
5902                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5903                 
5904                 if (oSubmenu) {
5905                     oSubmenu.subscribe.apply(oSubmenu, arguments);
5906                 }
5907                 else {
5908                     oItem.cfg.subscribeToConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments);
5909                 }
5910
5911             }
5912             while (i--);
5913         
5914         }
5915
5916     }
5917
5918 },
5919
5920
5921 unsubscribe: function () {
5922
5923     //  Remove the event for this Menu instance
5924     Menu.superclass.unsubscribe.apply(this, arguments);
5925
5926     //  Remove the "itemAdded" event so that all future submenus don't have 
5927     //  the event handler
5928     Menu.superclass.unsubscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments);
5929
5930
5931     var aItems = this.getItems(),
5932         nItems,
5933         oItem,
5934         oSubmenu,
5935         i;
5936         
5937
5938     if (aItems) {
5939
5940         nItems = aItems.length;
5941         
5942         if (nItems > 0) {
5943         
5944             i = nItems - 1;
5945             
5946             do {
5947
5948                 oItem = aItems[i];
5949                 oSubmenu = oItem.cfg.getProperty(_SUBMENU);
5950                 
5951                 if (oSubmenu) {
5952                     oSubmenu.unsubscribe.apply(oSubmenu, arguments);
5953                 }
5954                 else {
5955                     oItem.cfg.unsubscribeFromConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments);
5956                 }
5957
5958             }
5959             while (i--);
5960         
5961         }
5962
5963     }
5964
5965 },
5966
5967
5968 /**
5969 * @description Initializes the class's configurable properties which can be
5970 * changed using the menu's Config object ("cfg").
5971 * @method initDefaultConfig
5972 */
5973 initDefaultConfig: function () {
5974
5975     Menu.superclass.initDefaultConfig.call(this);
5976
5977     var oConfig = this.cfg;
5978
5979
5980     // Module documentation overrides
5981
5982     /**
5983     * @config effect
5984     * @description Object or array of objects representing the ContainerEffect 
5985     * classes that are active for animating the container.  When set this 
5986     * property is automatically applied to all submenus.
5987     * @type Object
5988     * @default null
5989     */
5990
5991     // Overlay documentation overrides
5992
5993
5994     /**
5995     * @config x
5996     * @description Number representing the absolute x-coordinate position of 
5997     * the Menu.  This property is only applied when the "position" 
5998     * configuration property is set to dynamic.
5999     * @type Number
6000     * @default null
6001     */
6002     
6003
6004     /**
6005     * @config y
6006     * @description Number representing the absolute y-coordinate position of 
6007     * the Menu.  This property is only applied when the "position" 
6008     * configuration property is set to dynamic.
6009     * @type Number
6010     * @default null
6011     */
6012
6013
6014     /**
6015     * @description Array of the absolute x and y positions of the Menu.  This 
6016     * property is only applied when the "position" configuration property is 
6017     * set to dynamic.
6018     * @config xy
6019     * @type Number[]
6020     * @default null
6021     */
6022     
6023
6024     /**
6025     * @config context
6026     * @description Array of context arguments for context-sensitive positioning.  
6027     * The format is: [id or element, element corner, context corner]. 
6028     * For example, setting this property to ["img1", "tl", "bl"] would 
6029     * align the Menu's top left corner to the context element's 
6030     * bottom left corner.  This property is only applied when the "position" 
6031     * configuration property is set to dynamic.
6032     * @type Array
6033     * @default null
6034     */
6035     
6036     
6037     /**
6038     * @config fixedcenter
6039     * @description Boolean indicating if the Menu should be anchored to the 
6040     * center of the viewport.  This property is only applied when the 
6041     * "position" configuration property is set to dynamic.
6042     * @type Boolean
6043     * @default false
6044     */
6045     
6046     
6047     /**
6048     * @config iframe
6049     * @description Boolean indicating whether or not the Menu should 
6050     * have an IFRAME shim; used to prevent SELECT elements from 
6051     * poking through an Overlay instance in IE6.  When set to "true", 
6052     * the iframe shim is created when the Menu instance is intially
6053     * made visible.  This property is only applied when the "position" 
6054     * configuration property is set to dynamic and is automatically applied 
6055     * to all submenus.
6056     * @type Boolean
6057     * @default true for IE6 and below, false for all other browsers.
6058     */
6059
6060
6061     // Add configuration attributes
6062
6063     /*
6064         Change the default value for the "visible" configuration 
6065         property to "false" by re-adding the property.
6066     */
6067
6068     /**
6069     * @config visible
6070     * @description Boolean indicating whether or not the menu is visible.  If 
6071     * the menu's "position" configuration property is set to "dynamic" (the 
6072     * default), this property toggles the menu's <code>&#60;div&#62;</code> 
6073     * element's "visibility" style property between "visible" (true) or 
6074     * "hidden" (false).  If the menu's "position" configuration property is 
6075     * set to "static" this property toggles the menu's 
6076     * <code>&#60;div&#62;</code> element's "display" style property 
6077     * between "block" (true) or "none" (false).
6078     * @default false
6079     * @type Boolean
6080     */
6081     oConfig.addProperty(
6082         VISIBLE_CONFIG.key, 
6083         {
6084             handler: this.configVisible, 
6085             value: VISIBLE_CONFIG.value, 
6086             validator: VISIBLE_CONFIG.validator
6087         }
6088      );
6089
6090
6091     /*
6092         Change the default value for the "constraintoviewport" configuration 
6093         property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property.
6094     */
6095
6096     /**
6097     * @config constraintoviewport
6098     * @description Boolean indicating if the menu will try to remain inside 
6099     * the boundaries of the size of viewport.  This property is only applied 
6100     * when the "position" configuration property is set to dynamic and is 
6101     * automatically applied to all submenus.
6102     * @default true
6103     * @type Boolean
6104     */
6105     oConfig.addProperty(
6106         CONSTRAIN_TO_VIEWPORT_CONFIG.key, 
6107         {
6108             handler: this.configConstrainToViewport, 
6109             value: CONSTRAIN_TO_VIEWPORT_CONFIG.value, 
6110             validator: CONSTRAIN_TO_VIEWPORT_CONFIG.validator, 
6111             supercedes: CONSTRAIN_TO_VIEWPORT_CONFIG.supercedes 
6112         } 
6113     );
6114
6115
6116     /*
6117         Change the default value for the "preventcontextoverlap" configuration 
6118         property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property.
6119     */
6120
6121     /**
6122     * @config preventcontextoverlap
6123     * @description Boolean indicating whether or not a submenu should overlap its parent MenuItem 
6124     * when the "constraintoviewport" configuration property is set to "true".
6125     * @type Boolean
6126     * @default true
6127     */
6128     oConfig.addProperty(PREVENT_CONTEXT_OVERLAP_CONFIG.key, {
6129
6130         value: PREVENT_CONTEXT_OVERLAP_CONFIG.value, 
6131         validator: PREVENT_CONTEXT_OVERLAP_CONFIG.validator, 
6132         supercedes: PREVENT_CONTEXT_OVERLAP_CONFIG.supercedes
6133
6134     });
6135
6136
6137     /**
6138     * @config position
6139     * @description String indicating how a menu should be positioned on the 
6140     * screen.  Possible values are "static" and "dynamic."  Static menus are 
6141     * visible by default and reside in the normal flow of the document 
6142     * (CSS position: static).  Dynamic menus are hidden by default, reside 
6143     * out of the normal flow of the document (CSS position: absolute), and 
6144     * can overlay other elements on the screen.
6145     * @default dynamic
6146     * @type String
6147     */
6148     oConfig.addProperty(
6149         POSITION_CONFIG.key, 
6150         {
6151             handler: this.configPosition,
6152             value: POSITION_CONFIG.value, 
6153             validator: POSITION_CONFIG.validator,
6154             supercedes: POSITION_CONFIG.supercedes
6155         }
6156     );
6157
6158
6159     /**
6160     * @config submenualignment
6161     * @description Array defining how submenus should be aligned to their 
6162     * parent menu item. The format is: [itemCorner, submenuCorner]. By default
6163     * a submenu's top left corner is aligned to its parent menu item's top 
6164     * right corner.
6165     * @default ["tl","tr"]
6166     * @type Array
6167     */
6168     oConfig.addProperty(
6169         SUBMENU_ALIGNMENT_CONFIG.key, 
6170         { 
6171             value: SUBMENU_ALIGNMENT_CONFIG.value,
6172             suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent
6173         }
6174     );
6175
6176
6177     /**
6178     * @config autosubmenudisplay
6179     * @description Boolean indicating if submenus are automatically made 
6180     * visible when the user mouses over the menu's items.
6181     * @default true
6182     * @type Boolean
6183     */
6184     oConfig.addProperty(
6185        AUTO_SUBMENU_DISPLAY_CONFIG.key, 
6186        { 
6187            value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 
6188            validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator,
6189            suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent
6190        } 
6191     );
6192
6193
6194     /**
6195     * @config showdelay
6196     * @description Number indicating the time (in milliseconds) that should 
6197     * expire before a submenu is made visible when the user mouses over 
6198     * the menu's items.  This property is only applied when the "position" 
6199     * configuration property is set to dynamic and is automatically applied 
6200     * to all submenus.
6201     * @default 250
6202     * @type Number
6203     */
6204     oConfig.addProperty(
6205        SHOW_DELAY_CONFIG.key, 
6206        { 
6207            value: SHOW_DELAY_CONFIG.value, 
6208            validator: SHOW_DELAY_CONFIG.validator,
6209            suppressEvent: SHOW_DELAY_CONFIG.suppressEvent
6210        } 
6211     );
6212
6213
6214     /**
6215     * @config hidedelay
6216     * @description Number indicating the time (in milliseconds) that should 
6217     * expire before the menu is hidden.  This property is only applied when 
6218     * the "position" configuration property is set to dynamic and is 
6219     * automatically applied to all submenus.
6220     * @default 0
6221     * @type Number
6222     */
6223     oConfig.addProperty(
6224        HIDE_DELAY_CONFIG.key, 
6225        { 
6226            handler: this.configHideDelay,
6227            value: HIDE_DELAY_CONFIG.value, 
6228            validator: HIDE_DELAY_CONFIG.validator, 
6229            suppressEvent: HIDE_DELAY_CONFIG.suppressEvent
6230        } 
6231     );
6232
6233
6234     /**
6235     * @config submenuhidedelay
6236     * @description Number indicating the time (in milliseconds) that should 
6237     * expire before a submenu is hidden when the user mouses out of a menu item 
6238     * heading in the direction of a submenu.  The value must be greater than or 
6239     * equal to the value specified for the "showdelay" configuration property.
6240     * This property is only applied when the "position" configuration property 
6241     * is set to dynamic and is automatically applied to all submenus.
6242     * @default 250
6243     * @type Number
6244     */
6245     oConfig.addProperty(
6246        SUBMENU_HIDE_DELAY_CONFIG.key, 
6247        { 
6248            value: SUBMENU_HIDE_DELAY_CONFIG.value, 
6249            validator: SUBMENU_HIDE_DELAY_CONFIG.validator,
6250            suppressEvent: SUBMENU_HIDE_DELAY_CONFIG.suppressEvent
6251        } 
6252     );
6253
6254
6255     /**
6256     * @config clicktohide
6257     * @description Boolean indicating if the menu will automatically be 
6258     * hidden if the user clicks outside of it.  This property is only 
6259     * applied when the "position" configuration property is set to dynamic 
6260     * and is automatically applied to all submenus.
6261     * @default true
6262     * @type Boolean
6263     */
6264     oConfig.addProperty(
6265         CLICK_TO_HIDE_CONFIG.key,
6266         {
6267             value: CLICK_TO_HIDE_CONFIG.value,
6268             validator: CLICK_TO_HIDE_CONFIG.validator,
6269             suppressEvent: CLICK_TO_HIDE_CONFIG.suppressEvent
6270         }
6271     );
6272
6273
6274     /**
6275     * @config container
6276     * @description HTML element reference or string specifying the id 
6277     * attribute of the HTML element that the menu's markup should be 
6278     * rendered into.
6279     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
6280     * level-one-html.html#ID-58190037">HTMLElement</a>|String
6281     * @default document.body
6282     */
6283     oConfig.addProperty(
6284        CONTAINER_CONFIG.key, 
6285        { 
6286            handler: this.configContainer,
6287            value: document.body,
6288            suppressEvent: CONTAINER_CONFIG.suppressEvent
6289        } 
6290    );
6291
6292
6293     /**
6294     * @config scrollincrement
6295     * @description Number used to control the scroll speed of a menu.  Used to 
6296     * increment the "scrollTop" property of the menu's body by when a menu's 
6297     * content is scrolling.  When set this property is automatically applied 
6298     * to all submenus.
6299     * @default 1
6300     * @type Number
6301     */
6302     oConfig.addProperty(
6303         SCROLL_INCREMENT_CONFIG.key, 
6304         { 
6305             value: SCROLL_INCREMENT_CONFIG.value, 
6306             validator: SCROLL_INCREMENT_CONFIG.validator,
6307             supercedes: SCROLL_INCREMENT_CONFIG.supercedes,
6308             suppressEvent: SCROLL_INCREMENT_CONFIG.suppressEvent
6309         }
6310     );
6311
6312
6313     /**
6314     * @config minscrollheight
6315     * @description Number defining the minimum threshold for the "maxheight" 
6316     * configuration property.  When set this property is automatically applied 
6317     * to all submenus.
6318     * @default 90
6319     * @type Number
6320     */
6321     oConfig.addProperty(
6322         MIN_SCROLL_HEIGHT_CONFIG.key, 
6323         { 
6324             value: MIN_SCROLL_HEIGHT_CONFIG.value, 
6325             validator: MIN_SCROLL_HEIGHT_CONFIG.validator,
6326             supercedes: MIN_SCROLL_HEIGHT_CONFIG.supercedes,
6327             suppressEvent: MIN_SCROLL_HEIGHT_CONFIG.suppressEvent
6328         }
6329     );
6330
6331
6332     /**
6333     * @config maxheight
6334     * @description Number defining the maximum height (in pixels) for a menu's 
6335     * body element (<code>&#60;div class="bd"&#62;</code>).  Once a menu's body 
6336     * exceeds this height, the contents of the body are scrolled to maintain 
6337     * this value.  This value cannot be set lower than the value of the 
6338     * "minscrollheight" configuration property.
6339     * @default 0
6340     * @type Number
6341     */
6342     oConfig.addProperty(
6343        MAX_HEIGHT_CONFIG.key, 
6344        {
6345             handler: this.configMaxHeight,
6346             value: MAX_HEIGHT_CONFIG.value,
6347             validator: MAX_HEIGHT_CONFIG.validator,
6348             suppressEvent: MAX_HEIGHT_CONFIG.suppressEvent,
6349             supercedes: MAX_HEIGHT_CONFIG.supercedes            
6350        } 
6351     );
6352
6353
6354     /**
6355     * @config classname
6356     * @description String representing the CSS class to be applied to the 
6357     * menu's root <code>&#60;div&#62;</code> element.  The specified class(es)  
6358     * are appended in addition to the default class as specified by the menu's
6359     * CSS_CLASS_NAME constant. When set this property is automatically 
6360     * applied to all submenus.
6361     * @default null
6362     * @type String
6363     */
6364     oConfig.addProperty(
6365         CLASS_NAME_CONFIG.key, 
6366         { 
6367             handler: this.configClassName,
6368             value: CLASS_NAME_CONFIG.value, 
6369             validator: CLASS_NAME_CONFIG.validator,
6370             supercedes: CLASS_NAME_CONFIG.supercedes      
6371         }
6372     );
6373
6374
6375     /**
6376     * @config disabled
6377     * @description Boolean indicating if the menu should be disabled.  
6378     * Disabling a menu disables each of its items.  (Disabled menu items are 
6379     * dimmed and will not respond to user input or fire events.)  Disabled
6380     * menus have a corresponding "disabled" CSS class applied to their root
6381     * <code>&#60;div&#62;</code> element.
6382     * @default false
6383     * @type Boolean
6384     */
6385     oConfig.addProperty(
6386         DISABLED_CONFIG.key, 
6387         { 
6388             handler: this.configDisabled,
6389             value: DISABLED_CONFIG.value, 
6390             validator: DISABLED_CONFIG.validator,
6391             suppressEvent: DISABLED_CONFIG.suppressEvent
6392         }
6393     );
6394
6395
6396     /**
6397     * @config shadow
6398     * @description Boolean indicating if the menu should have a shadow.
6399     * @default true
6400     * @type Boolean
6401     */
6402     oConfig.addProperty(
6403         SHADOW_CONFIG.key, 
6404         { 
6405             handler: this.configShadow,
6406             value: SHADOW_CONFIG.value, 
6407             validator: SHADOW_CONFIG.validator
6408         }
6409     );
6410
6411
6412     /**
6413     * @config keepopen
6414     * @description Boolean indicating if the menu should remain open when clicked.
6415     * @default false
6416     * @type Boolean
6417     */
6418     oConfig.addProperty(
6419         KEEP_OPEN_CONFIG.key, 
6420         { 
6421             value: KEEP_OPEN_CONFIG.value, 
6422             validator: KEEP_OPEN_CONFIG.validator
6423         }
6424     );
6425
6426 }
6427
6428 }); // END YAHOO.lang.extend
6429
6430 })();
6431
6432
6433
6434 (function () {
6435
6436 /**
6437 * Creates an item for a menu.
6438
6439 * @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.
6440 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6441 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 
6442 * the <code>&#60;li&#62;</code> element of the menu item.
6443 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6444 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
6445 * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
6446 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6447 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 
6448 * specifying the <code>&#60;option&#62;</code> element of the menu item.
6449 * @param {Object} p_oConfig Optional. Object literal specifying the 
6450 * configuration for the menu item. See configuration class documentation 
6451 * for more details.
6452 * @class MenuItem
6453 * @constructor
6454 */
6455 YAHOO.widget.MenuItem = function (p_oObject, p_oConfig) {
6456
6457     if (p_oObject) {
6458
6459         if (p_oConfig) {
6460     
6461             this.parent = p_oConfig.parent;
6462             this.value = p_oConfig.value;
6463             this.id = p_oConfig.id;
6464
6465         }
6466
6467         this.init(p_oObject, p_oConfig);
6468
6469     }
6470
6471 };
6472
6473
6474 var Dom = YAHOO.util.Dom,
6475     Module = YAHOO.widget.Module,
6476     Menu = YAHOO.widget.Menu,
6477     MenuItem = YAHOO.widget.MenuItem,
6478     CustomEvent = YAHOO.util.CustomEvent,
6479     UA = YAHOO.env.ua,
6480     Lang = YAHOO.lang,
6481
6482     // Private string constants
6483
6484     _TEXT = "text",
6485     _HASH = "#",
6486     _HYPHEN = "-",
6487     _HELP_TEXT = "helptext",
6488     _URL = "url",
6489     _TARGET = "target",
6490     _EMPHASIS = "emphasis",
6491     _STRONG_EMPHASIS = "strongemphasis",
6492     _CHECKED = "checked",
6493     _SUBMENU = "submenu",
6494     _DISABLED = "disabled",
6495     _SELECTED = "selected",
6496     _HAS_SUBMENU = "hassubmenu",
6497     _CHECKED_DISABLED = "checked-disabled",
6498     _HAS_SUBMENU_DISABLED = "hassubmenu-disabled",
6499     _HAS_SUBMENU_SELECTED = "hassubmenu-selected",
6500     _CHECKED_SELECTED = "checked-selected",
6501     _ONCLICK = "onclick",
6502     _CLASSNAME = "classname",
6503     _EMPTY_STRING = "",
6504     _OPTION = "OPTION",
6505     _OPTGROUP = "OPTGROUP",
6506     _LI_UPPERCASE = "LI",
6507     _HREF = "href",
6508     _SELECT = "SELECT",
6509     _DIV = "DIV",
6510     _START_HELP_TEXT = "<em class=\"helptext\">",
6511     _START_EM = "<em>",
6512     _END_EM = "</em>",
6513     _START_STRONG = "<strong>",
6514     _END_STRONG = "</strong>",
6515     _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
6516     _OBJ = "obj",
6517     _SCOPE = "scope",
6518     _NONE = "none",
6519     _VISIBLE = "visible",
6520     _SPACE = " ",
6521     _MENUITEM = "MenuItem",
6522     _CLICK = "click",
6523     _SHOW = "show",
6524     _HIDE = "hide",
6525     _LI_LOWERCASE = "li",
6526     _ANCHOR_TEMPLATE = "<a href=\"#\"></a>",
6527
6528     EVENT_TYPES = [
6529     
6530         ["mouseOverEvent", "mouseover"],
6531         ["mouseOutEvent", "mouseout"],
6532         ["mouseDownEvent", "mousedown"],
6533         ["mouseUpEvent", "mouseup"],
6534         ["clickEvent", _CLICK],
6535         ["keyPressEvent", "keypress"],
6536         ["keyDownEvent", "keydown"],
6537         ["keyUpEvent", "keyup"],
6538         ["focusEvent", "focus"],
6539         ["blurEvent", "blur"],
6540         ["destroyEvent", "destroy"]
6541     
6542     ],
6543
6544     TEXT_CONFIG = { 
6545         key: _TEXT, 
6546         value: _EMPTY_STRING, 
6547         validator: Lang.isString, 
6548         suppressEvent: true 
6549     }, 
6550
6551     HELP_TEXT_CONFIG = { 
6552         key: _HELP_TEXT,
6553         supercedes: [_TEXT], 
6554         suppressEvent: true 
6555     },
6556
6557     URL_CONFIG = { 
6558         key: _URL, 
6559         value: _HASH, 
6560         suppressEvent: true 
6561     }, 
6562
6563     TARGET_CONFIG = { 
6564         key: _TARGET, 
6565         suppressEvent: true 
6566     }, 
6567
6568     EMPHASIS_CONFIG = { 
6569         key: _EMPHASIS, 
6570         value: false, 
6571         validator: Lang.isBoolean, 
6572         suppressEvent: true, 
6573         supercedes: [_TEXT]
6574     }, 
6575
6576     STRONG_EMPHASIS_CONFIG = { 
6577         key: _STRONG_EMPHASIS, 
6578         value: false, 
6579         validator: Lang.isBoolean, 
6580         suppressEvent: true,
6581         supercedes: [_TEXT]
6582     },
6583
6584     CHECKED_CONFIG = { 
6585         key: _CHECKED, 
6586         value: false, 
6587         validator: Lang.isBoolean, 
6588         suppressEvent: true, 
6589         supercedes: [_DISABLED, _SELECTED]
6590     }, 
6591
6592     SUBMENU_CONFIG = { 
6593         key: _SUBMENU,
6594         suppressEvent: true,
6595         supercedes: [_DISABLED, _SELECTED]
6596     },
6597
6598     DISABLED_CONFIG = { 
6599         key: _DISABLED, 
6600         value: false, 
6601         validator: Lang.isBoolean, 
6602         suppressEvent: true,
6603         supercedes: [_TEXT, _SELECTED]
6604     },
6605
6606     SELECTED_CONFIG = { 
6607         key: _SELECTED, 
6608         value: false, 
6609         validator: Lang.isBoolean, 
6610         suppressEvent: true
6611     },
6612
6613     ONCLICK_CONFIG = { 
6614         key: _ONCLICK,
6615         suppressEvent: true
6616     },
6617
6618     CLASS_NAME_CONFIG = { 
6619         key: _CLASSNAME, 
6620         value: null, 
6621         validator: Lang.isString,
6622         suppressEvent: true
6623     },
6624     
6625     KEY_LISTENER_CONFIG = {
6626         key: "keylistener", 
6627         value: null, 
6628         suppressEvent: true
6629     },
6630
6631     m_oMenuItemTemplate = null,
6632
6633     CLASS_NAMES = {};
6634
6635
6636 /**
6637 * @method getClassNameForState
6638 * @description Returns a class name for the specified prefix and state.  If the class name does not 
6639 * yet exist, it is created and stored in the CLASS_NAMES object to increase performance.
6640 * @private
6641 * @param {String} prefix String representing the prefix for the class name
6642 * @param {String} state String representing a state - "disabled," "checked," etc.
6643 */  
6644 var getClassNameForState = function (prefix, state) {
6645
6646     var oClassNames = CLASS_NAMES[prefix];
6647     
6648     if (!oClassNames) {
6649         CLASS_NAMES[prefix] = {};
6650         oClassNames = CLASS_NAMES[prefix];
6651     }
6652
6653
6654     var sClassName = oClassNames[state];
6655
6656     if (!sClassName) {
6657         sClassName = prefix + _HYPHEN + state;
6658         oClassNames[state] = sClassName;
6659     }
6660
6661     return sClassName;
6662     
6663 };
6664
6665
6666 /**
6667 * @method addClassNameForState
6668 * @description Applies a class name to a MenuItem instance's &#60;LI&#62; and &#60;A&#62; elements
6669 * that represents a MenuItem's state - "disabled," "checked," etc.
6670 * @private
6671 * @param {String} state String representing a state - "disabled," "checked," etc.
6672 */  
6673 var addClassNameForState = function (state) {
6674
6675     Dom.addClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state));
6676     Dom.addClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state));
6677
6678 };
6679
6680 /**
6681 * @method removeClassNameForState
6682 * @description Removes a class name from a MenuItem instance's &#60;LI&#62; and &#60;A&#62; elements
6683 * that represents a MenuItem's state - "disabled," "checked," etc.
6684 * @private
6685 * @param {String} state String representing a state - "disabled," "checked," etc.
6686 */  
6687 var removeClassNameForState = function (state) {
6688
6689     Dom.removeClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state));
6690     Dom.removeClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state));
6691
6692 };
6693
6694
6695 MenuItem.prototype = {
6696
6697     /**
6698     * @property CSS_CLASS_NAME
6699     * @description String representing the CSS class(es) to be applied to the 
6700     * <code>&#60;li&#62;</code> element of the menu item.
6701     * @default "yuimenuitem"
6702     * @final
6703     * @type String
6704     */
6705     CSS_CLASS_NAME: "yuimenuitem",
6706
6707
6708     /**
6709     * @property CSS_LABEL_CLASS_NAME
6710     * @description String representing the CSS class(es) to be applied to the 
6711     * menu item's <code>&#60;a&#62;</code> element.
6712     * @default "yuimenuitemlabel"
6713     * @final
6714     * @type String
6715     */
6716     CSS_LABEL_CLASS_NAME: "yuimenuitemlabel",
6717
6718
6719     /**
6720     * @property SUBMENU_TYPE
6721     * @description Object representing the type of menu to instantiate and 
6722     * add when parsing the child nodes of the menu item's source HTML element.
6723     * @final
6724     * @type YAHOO.widget.Menu
6725     */
6726     SUBMENU_TYPE: null,
6727
6728
6729
6730     // Private member variables
6731     
6732
6733     /**
6734     * @property _oAnchor
6735     * @description Object reference to the menu item's 
6736     * <code>&#60;a&#62;</code> element.
6737     * @default null 
6738     * @private
6739     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6740     * one-html.html#ID-48250443">HTMLAnchorElement</a>
6741     */
6742     _oAnchor: null,
6743     
6744     
6745     /**
6746     * @property _oHelpTextEM
6747     * @description Object reference to the menu item's help text 
6748     * <code>&#60;em&#62;</code> element.
6749     * @default null
6750     * @private
6751     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6752     * one-html.html#ID-58190037">HTMLElement</a>
6753     */
6754     _oHelpTextEM: null,
6755     
6756     
6757     /**
6758     * @property _oSubmenu
6759     * @description Object reference to the menu item's submenu.
6760     * @default null
6761     * @private
6762     * @type YAHOO.widget.Menu
6763     */
6764     _oSubmenu: null,
6765
6766
6767     /** 
6768     * @property _oOnclickAttributeValue
6769     * @description Object reference to the menu item's current value for the 
6770     * "onclick" configuration attribute.
6771     * @default null
6772     * @private
6773     * @type Object
6774     */
6775     _oOnclickAttributeValue: null,
6776
6777
6778     /**
6779     * @property _sClassName
6780     * @description The current value of the "classname" configuration attribute.
6781     * @default null
6782     * @private
6783     * @type String
6784     */
6785     _sClassName: null,
6786
6787
6788
6789     // Public properties
6790
6791
6792     /**
6793     * @property constructor
6794     * @description Object reference to the menu item's constructor function.
6795     * @default YAHOO.widget.MenuItem
6796     * @type YAHOO.widget.MenuItem
6797     */
6798     constructor: MenuItem,
6799
6800
6801     /**
6802     * @property index
6803     * @description Number indicating the ordinal position of the menu item in 
6804     * its group.
6805     * @default null
6806     * @type Number
6807     */
6808     index: null,
6809
6810
6811     /**
6812     * @property groupIndex
6813     * @description Number indicating the index of the group to which the menu 
6814     * item belongs.
6815     * @default null
6816     * @type Number
6817     */
6818     groupIndex: null,
6819
6820
6821     /**
6822     * @property parent
6823     * @description Object reference to the menu item's parent menu.
6824     * @default null
6825     * @type YAHOO.widget.Menu
6826     */
6827     parent: null,
6828
6829
6830     /**
6831     * @property element
6832     * @description Object reference to the menu item's 
6833     * <code>&#60;li&#62;</code> element.
6834     * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level
6835     * -one-html.html#ID-74680021">HTMLLIElement</a>
6836     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6837     * one-html.html#ID-74680021">HTMLLIElement</a>
6838     */
6839     element: null,
6840
6841
6842     /**
6843     * @property srcElement
6844     * @description Object reference to the HTML element (either 
6845     * <code>&#60;li&#62;</code>, <code>&#60;optgroup&#62;</code> or 
6846     * <code>&#60;option&#62;</code>) used create the menu item.
6847     * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
6848     * level-one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.
6849     * w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247"
6850     * >HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
6851     * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
6852     * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6853     * one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.w3.
6854     * org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247">
6855     * HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
6856     * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
6857     */
6858     srcElement: null,
6859
6860
6861     /**
6862     * @property value
6863     * @description Object reference to the menu item's value.
6864     * @default null
6865     * @type Object
6866     */
6867     value: null,
6868
6869
6870     /**
6871     * @property browser
6872     * @deprecated Use YAHOO.env.ua
6873     * @description String representing the browser.
6874     * @type String
6875     */
6876     browser: Module.prototype.browser,
6877
6878
6879     /**
6880     * @property id
6881     * @description Id of the menu item's root <code>&#60;li&#62;</code> 
6882     * element.  This property should be set via the constructor using the 
6883     * configuration object literal.  If an id is not specified, then one will 
6884     * be created using the "generateId" method of the Dom utility.
6885     * @default null
6886     * @type String
6887     */
6888     id: null,
6889
6890
6891
6892     // Events
6893
6894
6895     /**
6896     * @event destroyEvent
6897     * @description Fires when the menu item's <code>&#60;li&#62;</code> 
6898     * element is removed from its parent <code>&#60;ul&#62;</code> element.
6899     * @type YAHOO.util.CustomEvent
6900     */
6901
6902
6903     /**
6904     * @event mouseOverEvent
6905     * @description Fires when the mouse has entered the menu item.  Passes 
6906     * back the DOM Event object as an argument.
6907     * @type YAHOO.util.CustomEvent
6908     */
6909
6910
6911     /**
6912     * @event mouseOutEvent
6913     * @description Fires when the mouse has left the menu item.  Passes back 
6914     * the DOM Event object as an argument.
6915     * @type YAHOO.util.CustomEvent
6916     */
6917
6918
6919     /**
6920     * @event mouseDownEvent
6921     * @description Fires when the user mouses down on the menu item.  Passes 
6922     * back the DOM Event object as an argument.
6923     * @type YAHOO.util.CustomEvent
6924     */
6925
6926
6927     /**
6928     * @event mouseUpEvent
6929     * @description Fires when the user releases a mouse button while the mouse 
6930     * is over the menu item.  Passes back the DOM Event object as an argument.
6931     * @type YAHOO.util.CustomEvent
6932     */
6933
6934
6935     /**
6936     * @event clickEvent
6937     * @description Fires when the user clicks the on the menu item.  Passes 
6938     * back the DOM Event object as an argument.
6939     * @type YAHOO.util.CustomEvent
6940     */
6941
6942
6943     /**
6944     * @event keyPressEvent
6945     * @description Fires when the user presses an alphanumeric key when the 
6946     * menu item has focus.  Passes back the DOM Event object as an argument.
6947     * @type YAHOO.util.CustomEvent
6948     */
6949
6950
6951     /**
6952     * @event keyDownEvent
6953     * @description Fires when the user presses a key when the menu item has 
6954     * focus.  Passes back the DOM Event object as an argument.
6955     * @type YAHOO.util.CustomEvent
6956     */
6957
6958
6959     /**
6960     * @event keyUpEvent
6961     * @description Fires when the user releases a key when the menu item has 
6962     * focus.  Passes back the DOM Event object as an argument.
6963     * @type YAHOO.util.CustomEvent
6964     */
6965
6966
6967     /**
6968     * @event focusEvent
6969     * @description Fires when the menu item receives focus.
6970     * @type YAHOO.util.CustomEvent
6971     */
6972
6973
6974     /**
6975     * @event blurEvent
6976     * @description Fires when the menu item loses the input focus.
6977     * @type YAHOO.util.CustomEvent
6978     */
6979
6980
6981     /**
6982     * @method init
6983     * @description The MenuItem class's initialization method. This method is 
6984     * automatically called by the constructor, and sets up all DOM references 
6985     * for pre-existing markup, and creates required markup if it is not 
6986     * already present.
6987     * @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.
6988     * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6989     * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 
6990     * the <code>&#60;li&#62;</code> element of the menu item.
6991     * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6992     * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
6993     * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
6994     * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6995     * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 
6996     * specifying the <code>&#60;option&#62;</code> element of the menu item.
6997     * @param {Object} p_oConfig Optional. Object literal specifying the 
6998     * configuration for the menu item. See configuration class documentation 
6999     * for more details.
7000     */
7001     init: function (p_oObject, p_oConfig) {
7002
7003
7004         if (!this.SUBMENU_TYPE) {
7005     
7006             this.SUBMENU_TYPE = Menu;
7007     
7008         }
7009
7010
7011         // Create the config object
7012
7013         this.cfg = new YAHOO.util.Config(this);
7014
7015         this.initDefaultConfig();
7016
7017         var oConfig = this.cfg,
7018             sURL = _HASH,
7019             oCustomEvent,
7020             aEventData,
7021             oAnchor,
7022             sTarget,
7023             sText,
7024             sId,
7025             i;
7026
7027
7028         if (Lang.isString(p_oObject)) {
7029
7030             this._createRootNodeStructure();
7031
7032             oConfig.queueProperty(_TEXT, p_oObject);
7033
7034         }
7035         else if (p_oObject && p_oObject.tagName) {
7036
7037             switch(p_oObject.tagName.toUpperCase()) {
7038
7039                 case _OPTION:
7040
7041                     this._createRootNodeStructure();
7042
7043                     oConfig.queueProperty(_TEXT, p_oObject.text);
7044                     oConfig.queueProperty(_DISABLED, p_oObject.disabled);
7045
7046                     this.value = p_oObject.value;
7047
7048                     this.srcElement = p_oObject;
7049
7050                 break;
7051
7052                 case _OPTGROUP:
7053
7054                     this._createRootNodeStructure();
7055
7056                     oConfig.queueProperty(_TEXT, p_oObject.label);
7057                     oConfig.queueProperty(_DISABLED, p_oObject.disabled);
7058
7059                     this.srcElement = p_oObject;
7060
7061                     this._initSubTree();
7062
7063                 break;
7064
7065                 case _LI_UPPERCASE:
7066
7067                     // Get the anchor node (if it exists)
7068                     
7069                     oAnchor = Dom.getFirstChild(p_oObject);
7070
7071
7072                     // Capture the "text" and/or the "URL"
7073
7074                     if (oAnchor) {
7075
7076                         sURL = oAnchor.getAttribute(_HREF, 2);
7077                         sTarget = oAnchor.getAttribute(_TARGET);
7078
7079                         sText = oAnchor.innerHTML;
7080
7081                     }
7082
7083                     this.srcElement = p_oObject;
7084                     this.element = p_oObject;
7085                     this._oAnchor = oAnchor;
7086
7087                     /*
7088                         Set these properties silently to sync up the 
7089                         configuration object without making changes to the 
7090                         element's DOM
7091                     */ 
7092
7093                     oConfig.setProperty(_TEXT, sText, true);
7094                     oConfig.setProperty(_URL, sURL, true);
7095                     oConfig.setProperty(_TARGET, sTarget, true);
7096
7097                     this._initSubTree();
7098
7099                 break;
7100
7101             }            
7102
7103         }
7104
7105
7106         if (this.element) {
7107
7108             sId = (this.srcElement || this.element).id;
7109
7110             if (!sId) {
7111
7112                 sId = this.id || Dom.generateId();
7113
7114                 this.element.id = sId;
7115
7116             }
7117
7118             this.id = sId;
7119
7120
7121             Dom.addClass(this.element, this.CSS_CLASS_NAME);
7122             Dom.addClass(this._oAnchor, this.CSS_LABEL_CLASS_NAME);
7123
7124
7125             i = EVENT_TYPES.length - 1;
7126
7127             do {
7128
7129                 aEventData = EVENT_TYPES[i];
7130
7131                 oCustomEvent = this.createEvent(aEventData[1]);
7132                 oCustomEvent.signature = CustomEvent.LIST;
7133                 
7134                 this[aEventData[0]] = oCustomEvent;
7135
7136             }
7137             while (i--);
7138
7139
7140             if (p_oConfig) {
7141     
7142                 oConfig.applyConfig(p_oConfig);
7143     
7144             }        
7145
7146             oConfig.fireQueue();
7147
7148         }
7149
7150     },
7151
7152
7153
7154     // Private methods
7155
7156     /**
7157     * @method _createRootNodeStructure
7158     * @description Creates the core DOM structure for the menu item.
7159     * @private
7160     */
7161     _createRootNodeStructure: function () {
7162
7163         var oElement,
7164             oAnchor;
7165
7166         if (!m_oMenuItemTemplate) {
7167
7168             m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE);
7169             m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE;
7170
7171         }
7172
7173         oElement = m_oMenuItemTemplate.cloneNode(true);
7174         oElement.className = this.CSS_CLASS_NAME;
7175
7176         oAnchor = oElement.firstChild;
7177         oAnchor.className = this.CSS_LABEL_CLASS_NAME;
7178
7179         this.element = oElement;
7180         this._oAnchor = oAnchor;
7181
7182     },
7183
7184
7185     /**
7186     * @method _initSubTree
7187     * @description Iterates the source element's childNodes collection and uses 
7188     * the child nodes to instantiate other menus.
7189     * @private
7190     */
7191     _initSubTree: function () {
7192
7193         var oSrcEl = this.srcElement,
7194             oConfig = this.cfg,
7195             oNode,
7196             aOptions,
7197             nOptions,
7198             oMenu,
7199             n;
7200
7201
7202         if (oSrcEl.childNodes.length > 0) {
7203
7204             if (this.parent.lazyLoad && this.parent.srcElement && 
7205                 this.parent.srcElement.tagName.toUpperCase() == _SELECT) {
7206
7207                 oConfig.setProperty(
7208                         _SUBMENU, 
7209                         { id: Dom.generateId(), itemdata: oSrcEl.childNodes }
7210                     );
7211
7212             }
7213             else {
7214
7215                 oNode = oSrcEl.firstChild;
7216                 aOptions = [];
7217     
7218                 do {
7219     
7220                     if (oNode && oNode.tagName) {
7221     
7222                         switch(oNode.tagName.toUpperCase()) {
7223                 
7224                             case _DIV:
7225                 
7226                                 oConfig.setProperty(_SUBMENU, oNode);
7227                 
7228                             break;
7229          
7230                             case _OPTION:
7231         
7232                                 aOptions[aOptions.length] = oNode;
7233         
7234                             break;
7235                
7236                         }
7237                     
7238                     }
7239                 
7240                 }        
7241                 while((oNode = oNode.nextSibling));
7242     
7243     
7244                 nOptions = aOptions.length;
7245     
7246                 if (nOptions > 0) {
7247     
7248                     oMenu = new this.SUBMENU_TYPE(Dom.generateId());
7249                     
7250                     oConfig.setProperty(_SUBMENU, oMenu);
7251     
7252                     for(n=0; n<nOptions; n++) {
7253         
7254                         oMenu.addItem((new oMenu.ITEM_TYPE(aOptions[n])));
7255         
7256                     }
7257         
7258                 }
7259             
7260             }
7261
7262         }
7263
7264     },
7265
7266
7267
7268     // Event handlers for configuration properties
7269
7270
7271     /**
7272     * @method configText
7273     * @description Event handler for when the "text" configuration property of 
7274     * the menu item changes.
7275     * @param {String} p_sType String representing the name of the event that 
7276     * was fired.
7277     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7278     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7279     * that fired the event.
7280     */
7281     configText: function (p_sType, p_aArgs, p_oItem) {
7282
7283         var sText = p_aArgs[0],
7284             oConfig = this.cfg,
7285             oAnchor = this._oAnchor,
7286             sHelpText = oConfig.getProperty(_HELP_TEXT),
7287             sHelpTextHTML = _EMPTY_STRING,
7288             sEmphasisStartTag = _EMPTY_STRING,
7289             sEmphasisEndTag = _EMPTY_STRING;
7290
7291
7292         if (sText) {
7293
7294
7295             if (sHelpText) {
7296                     
7297                 sHelpTextHTML = _START_HELP_TEXT + sHelpText + _END_EM;
7298             
7299             }
7300
7301
7302             if (oConfig.getProperty(_EMPHASIS)) {
7303
7304                 sEmphasisStartTag = _START_EM;
7305                 sEmphasisEndTag = _END_EM;
7306
7307             }
7308
7309
7310             if (oConfig.getProperty(_STRONG_EMPHASIS)) {
7311
7312                 sEmphasisStartTag = _START_STRONG;
7313                 sEmphasisEndTag = _END_STRONG;
7314             
7315             }
7316
7317
7318             oAnchor.innerHTML = (sEmphasisStartTag + sText + sEmphasisEndTag + sHelpTextHTML);
7319
7320         }
7321
7322     },
7323
7324
7325     /**
7326     * @method configHelpText
7327     * @description Event handler for when the "helptext" configuration property 
7328     * of the menu item changes.
7329     * @param {String} p_sType String representing the name of the event that 
7330     * was fired.
7331     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7332     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7333     * that fired the event.
7334     */    
7335     configHelpText: function (p_sType, p_aArgs, p_oItem) {
7336
7337         this.cfg.refireEvent(_TEXT);
7338
7339     },
7340
7341
7342     /**
7343     * @method configURL
7344     * @description Event handler for when the "url" configuration property of 
7345     * the menu item changes.
7346     * @param {String} p_sType String representing the name of the event that 
7347     * was fired.
7348     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7349     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7350     * that fired the event.
7351     */    
7352     configURL: function (p_sType, p_aArgs, p_oItem) {
7353
7354         var sURL = p_aArgs[0];
7355
7356         if (!sURL) {
7357
7358             sURL = _HASH;
7359
7360         }
7361
7362         var oAnchor = this._oAnchor;
7363
7364         if (UA.opera) {
7365
7366             oAnchor.removeAttribute(_HREF);
7367         
7368         }
7369
7370         oAnchor.setAttribute(_HREF, sURL);
7371
7372     },
7373
7374
7375     /**
7376     * @method configTarget
7377     * @description Event handler for when the "target" configuration property 
7378     * of the menu item changes.  
7379     * @param {String} p_sType String representing the name of the event that 
7380     * was fired.
7381     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7382     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7383     * that fired the event.
7384     */    
7385     configTarget: function (p_sType, p_aArgs, p_oItem) {
7386
7387         var sTarget = p_aArgs[0],
7388             oAnchor = this._oAnchor;
7389
7390         if (sTarget && sTarget.length > 0) {
7391
7392             oAnchor.setAttribute(_TARGET, sTarget);
7393
7394         }
7395         else {
7396
7397             oAnchor.removeAttribute(_TARGET);
7398         
7399         }
7400
7401     },
7402
7403
7404     /**
7405     * @method configEmphasis
7406     * @description Event handler for when the "emphasis" configuration property
7407     * of the menu item changes.
7408     * @param {String} p_sType String representing the name of the event that 
7409     * was fired.
7410     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7411     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7412     * that fired the event.
7413     */    
7414     configEmphasis: function (p_sType, p_aArgs, p_oItem) {
7415
7416         var bEmphasis = p_aArgs[0],
7417             oConfig = this.cfg;
7418
7419
7420         if (bEmphasis && oConfig.getProperty(_STRONG_EMPHASIS)) {
7421
7422             oConfig.setProperty(_STRONG_EMPHASIS, false);
7423
7424         }
7425
7426
7427         oConfig.refireEvent(_TEXT);
7428
7429     },
7430
7431
7432     /**
7433     * @method configStrongEmphasis
7434     * @description Event handler for when the "strongemphasis" configuration 
7435     * property of the menu item changes.
7436     * @param {String} p_sType String representing the name of the event that 
7437     * was fired.
7438     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7439     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7440     * that fired the event.
7441     */    
7442     configStrongEmphasis: function (p_sType, p_aArgs, p_oItem) {
7443
7444         var bStrongEmphasis = p_aArgs[0],
7445             oConfig = this.cfg;
7446
7447
7448         if (bStrongEmphasis && oConfig.getProperty(_EMPHASIS)) {
7449
7450             oConfig.setProperty(_EMPHASIS, false);
7451
7452         }
7453
7454         oConfig.refireEvent(_TEXT);
7455
7456     },
7457
7458
7459     /**
7460     * @method configChecked
7461     * @description Event handler for when the "checked" configuration property 
7462     * of the menu item changes. 
7463     * @param {String} p_sType String representing the name of the event that 
7464     * was fired.
7465     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7466     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7467     * that fired the event.
7468     */    
7469     configChecked: function (p_sType, p_aArgs, p_oItem) {
7470
7471         var bChecked = p_aArgs[0],
7472             oConfig = this.cfg;
7473
7474
7475         if (bChecked) {
7476
7477             addClassNameForState.call(this, _CHECKED);
7478
7479         }
7480         else {
7481
7482             removeClassNameForState.call(this, _CHECKED);
7483         }
7484
7485
7486         oConfig.refireEvent(_TEXT);
7487
7488
7489         if (oConfig.getProperty(_DISABLED)) {
7490
7491             oConfig.refireEvent(_DISABLED);
7492
7493         }
7494
7495
7496         if (oConfig.getProperty(_SELECTED)) {
7497
7498             oConfig.refireEvent(_SELECTED);
7499
7500         }
7501
7502     },
7503
7504
7505
7506     /**
7507     * @method configDisabled
7508     * @description Event handler for when the "disabled" configuration property 
7509     * of the menu item changes. 
7510     * @param {String} p_sType String representing the name of the event that 
7511     * was fired.
7512     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7513     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7514     * that fired the event.
7515     */    
7516     configDisabled: function (p_sType, p_aArgs, p_oItem) {
7517
7518         var bDisabled = p_aArgs[0],
7519             oConfig = this.cfg,
7520             oSubmenu = oConfig.getProperty(_SUBMENU),
7521             bChecked = oConfig.getProperty(_CHECKED);
7522
7523
7524         if (bDisabled) {
7525
7526             if (oConfig.getProperty(_SELECTED)) {
7527
7528                 oConfig.setProperty(_SELECTED, false);
7529
7530             }
7531
7532
7533             addClassNameForState.call(this, _DISABLED);
7534
7535
7536             if (oSubmenu) {
7537
7538                 addClassNameForState.call(this, _HAS_SUBMENU_DISABLED);
7539             
7540             }
7541             
7542
7543             if (bChecked) {
7544
7545                 addClassNameForState.call(this, _CHECKED_DISABLED);
7546
7547             }
7548
7549         }
7550         else {
7551
7552             removeClassNameForState.call(this, _DISABLED);
7553
7554
7555             if (oSubmenu) {
7556
7557                 removeClassNameForState.call(this, _HAS_SUBMENU_DISABLED);
7558             
7559             }
7560             
7561
7562             if (bChecked) {
7563
7564                 removeClassNameForState.call(this, _CHECKED_DISABLED);
7565
7566             }
7567
7568         }
7569
7570     },
7571
7572
7573     /**
7574     * @method configSelected
7575     * @description Event handler for when the "selected" configuration property 
7576     * of the menu item changes. 
7577     * @param {String} p_sType String representing the name of the event that 
7578     * was fired.
7579     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7580     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7581     * that fired the event.
7582     */    
7583     configSelected: function (p_sType, p_aArgs, p_oItem) {
7584
7585         var oConfig = this.cfg,
7586             oAnchor = this._oAnchor,
7587             
7588             bSelected = p_aArgs[0],
7589             bChecked = oConfig.getProperty(_CHECKED),
7590             oSubmenu = oConfig.getProperty(_SUBMENU);
7591
7592
7593         if (UA.opera) {
7594
7595             oAnchor.blur();
7596         
7597         }
7598
7599
7600         if (bSelected && !oConfig.getProperty(_DISABLED)) {
7601
7602             addClassNameForState.call(this, _SELECTED);
7603
7604
7605             if (oSubmenu) {
7606
7607                 addClassNameForState.call(this, _HAS_SUBMENU_SELECTED);
7608             
7609             }
7610
7611
7612             if (bChecked) {
7613
7614                 addClassNameForState.call(this, _CHECKED_SELECTED);
7615
7616             }
7617
7618         }
7619         else {
7620
7621             removeClassNameForState.call(this, _SELECTED);
7622
7623
7624             if (oSubmenu) {
7625
7626                 removeClassNameForState.call(this, _HAS_SUBMENU_SELECTED);
7627             
7628             }
7629
7630
7631             if (bChecked) {
7632
7633                 removeClassNameForState.call(this, _CHECKED_SELECTED);
7634
7635             }
7636
7637         }
7638
7639
7640         if (this.hasFocus() && UA.opera) {
7641         
7642             oAnchor.focus();
7643         
7644         }
7645
7646     },
7647
7648
7649     /**
7650     * @method _onSubmenuBeforeHide
7651     * @description "beforehide" Custom Event handler for a submenu.
7652     * @private
7653     * @param {String} p_sType String representing the name of the event that 
7654     * was fired.
7655     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7656     */
7657     _onSubmenuBeforeHide: function (p_sType, p_aArgs) {
7658
7659         var oItem = this.parent,
7660             oMenu;
7661
7662         function onHide() {
7663
7664             oItem._oAnchor.blur();
7665             oMenu.beforeHideEvent.unsubscribe(onHide);
7666         
7667         }
7668
7669
7670         if (oItem.hasFocus()) {
7671
7672             oMenu = oItem.parent;
7673
7674             oMenu.beforeHideEvent.subscribe(onHide);
7675         
7676         }
7677     
7678     },
7679
7680
7681     /**
7682     * @method configSubmenu
7683     * @description Event handler for when the "submenu" configuration property 
7684     * of the menu item changes. 
7685     * @param {String} p_sType String representing the name of the event that 
7686     * was fired.
7687     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7688     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7689     * that fired the event.
7690     */
7691     configSubmenu: function (p_sType, p_aArgs, p_oItem) {
7692
7693         var oSubmenu = p_aArgs[0],
7694             oConfig = this.cfg,
7695             bLazyLoad = this.parent && this.parent.lazyLoad,
7696             oMenu,
7697             sSubmenuId,
7698             oSubmenuConfig;
7699
7700
7701         if (oSubmenu) {
7702
7703             if (oSubmenu instanceof Menu) {
7704
7705                 oMenu = oSubmenu;
7706                 oMenu.parent = this;
7707                 oMenu.lazyLoad = bLazyLoad;
7708
7709             }
7710             else if (Lang.isObject(oSubmenu) && oSubmenu.id && !oSubmenu.nodeType) {
7711
7712                 sSubmenuId = oSubmenu.id;
7713                 oSubmenuConfig = oSubmenu;
7714
7715                 oSubmenuConfig.lazyload = bLazyLoad;
7716                 oSubmenuConfig.parent = this;
7717
7718                 oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig);
7719
7720
7721                 // Set the value of the property to the Menu instance
7722
7723                 oConfig.setProperty(_SUBMENU, oMenu, true);
7724
7725             }
7726             else {
7727
7728                 oMenu = new this.SUBMENU_TYPE(oSubmenu, { lazyload: bLazyLoad, parent: this });
7729
7730
7731                 // Set the value of the property to the Menu instance
7732                 
7733                 oConfig.setProperty(_SUBMENU, oMenu, true);
7734
7735             }
7736
7737
7738             if (oMenu) {
7739
7740                 oMenu.cfg.setProperty(_PREVENT_CONTEXT_OVERLAP, true);
7741
7742                 addClassNameForState.call(this, _HAS_SUBMENU);
7743
7744
7745                 if (oConfig.getProperty(_URL) === _HASH) {
7746                 
7747                     oConfig.setProperty(_URL, (_HASH + oMenu.id));
7748                 
7749                 }
7750
7751
7752                 this._oSubmenu = oMenu;
7753
7754
7755                 if (UA.opera) {
7756                 
7757                     oMenu.beforeHideEvent.subscribe(this._onSubmenuBeforeHide);               
7758                 
7759                 }
7760             
7761             }
7762
7763         }
7764         else {
7765
7766             removeClassNameForState.call(this, _HAS_SUBMENU);
7767
7768             if (this._oSubmenu) {
7769
7770                 this._oSubmenu.destroy();
7771
7772             }
7773
7774         }
7775
7776
7777         if (oConfig.getProperty(_DISABLED)) {
7778
7779             oConfig.refireEvent(_DISABLED);
7780
7781         }
7782
7783
7784         if (oConfig.getProperty(_SELECTED)) {
7785
7786             oConfig.refireEvent(_SELECTED);
7787
7788         }
7789
7790     },
7791
7792
7793     /**
7794     * @method configOnClick
7795     * @description Event handler for when the "onclick" configuration property 
7796     * of the menu item changes. 
7797     * @param {String} p_sType String representing the name of the event that 
7798     * was fired.
7799     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7800     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7801     * that fired the event.
7802     */
7803     configOnClick: function (p_sType, p_aArgs, p_oItem) {
7804
7805         var oObject = p_aArgs[0];
7806
7807         /*
7808             Remove any existing listeners if a "click" event handler has 
7809             already been specified.
7810         */
7811
7812         if (this._oOnclickAttributeValue && (this._oOnclickAttributeValue != oObject)) {
7813
7814             this.clickEvent.unsubscribe(this._oOnclickAttributeValue.fn, 
7815                                 this._oOnclickAttributeValue.obj);
7816
7817             this._oOnclickAttributeValue = null;
7818
7819         }
7820
7821
7822         if (!this._oOnclickAttributeValue && Lang.isObject(oObject) && 
7823             Lang.isFunction(oObject.fn)) {
7824             
7825             this.clickEvent.subscribe(oObject.fn, 
7826                 ((_OBJ in oObject) ? oObject.obj : this), 
7827                 ((_SCOPE in oObject) ? oObject.scope : null) );
7828
7829             this._oOnclickAttributeValue = oObject;
7830
7831         }
7832     
7833     },
7834
7835
7836     /**
7837     * @method configClassName
7838     * @description Event handler for when the "classname" configuration 
7839     * property of a menu item changes.
7840     * @param {String} p_sType String representing the name of the event that 
7841     * was fired.
7842     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7843     * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
7844     * that fired the event.
7845     */
7846     configClassName: function (p_sType, p_aArgs, p_oItem) {
7847     
7848         var sClassName = p_aArgs[0];
7849     
7850         if (this._sClassName) {
7851     
7852             Dom.removeClass(this.element, this._sClassName);
7853     
7854         }
7855     
7856         Dom.addClass(this.element, sClassName);
7857         this._sClassName = sClassName;
7858     
7859     },
7860
7861
7862     /**
7863     * @method _dispatchClickEvent
7864     * @description Dispatches a DOM "click" event to the anchor element of a 
7865     * MenuItem instance.
7866     * @private  
7867     */
7868     _dispatchClickEvent: function () {
7869
7870         var oMenuItem = this,
7871             oAnchor;
7872
7873         if (!oMenuItem.cfg.getProperty(_DISABLED)) {
7874             oAnchor = Dom.getFirstChild(oMenuItem.element);
7875
7876             //  Dispatch a "click" event to the MenuItem's anchor so that its
7877             //  "click" event handlers will get called in response to the user 
7878             //  pressing the keyboard shortcut defined by the "keylistener"
7879             //  configuration property.
7880
7881             this._dispatchDOMClick(oAnchor);
7882         }
7883     },
7884
7885     /**
7886      * Utility method to dispatch a DOM click event on the HTMLElement passed in
7887      *
7888      * @method _dispatchDOMClick
7889      * @protected
7890      * @param {HTMLElement} el
7891      */    
7892     _dispatchDOMClick : function(el) {
7893         var oEvent;
7894
7895         // Choose the standards path for IE9
7896         if (UA.ie && UA.ie < 9) {
7897             el.fireEvent(_ONCLICK);
7898         } else {
7899             if ((UA.gecko && UA.gecko >= 1.9) || UA.opera || UA.webkit) {
7900                 oEvent = document.createEvent("HTMLEvents");
7901                 oEvent.initEvent(_CLICK, true, true);
7902             } else {
7903                 oEvent = document.createEvent("MouseEvents");
7904                 oEvent.initMouseEvent(_CLICK, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
7905             }
7906             el.dispatchEvent(oEvent);
7907         }
7908     },
7909
7910     /**
7911     * @method _createKeyListener
7912     * @description "show" event handler for a Menu instance - responsible for 
7913     * setting up the KeyListener instance for a MenuItem.
7914     * @private  
7915     * @param {String} type String representing the name of the event that 
7916     * was fired.
7917     * @param {Array} args Array of arguments sent when the event was fired.
7918     * @param {Array} keyData Array of arguments sent when the event was fired.
7919     */
7920     _createKeyListener: function (type, args, keyData) {
7921
7922         var oMenuItem = this,
7923             oMenu = oMenuItem.parent;
7924
7925         var oKeyListener = new YAHOO.util.KeyListener(
7926                                         oMenu.element.ownerDocument, 
7927                                         keyData, 
7928                                         {
7929                                             fn: oMenuItem._dispatchClickEvent, 
7930                                             scope: oMenuItem, 
7931                                             correctScope: true });
7932
7933
7934         if (oMenu.cfg.getProperty(_VISIBLE)) {
7935             oKeyListener.enable();
7936         }
7937
7938
7939         oMenu.subscribe(_SHOW, oKeyListener.enable, null, oKeyListener);
7940         oMenu.subscribe(_HIDE, oKeyListener.disable, null, oKeyListener);
7941         
7942         oMenuItem._keyListener = oKeyListener;
7943         
7944         oMenu.unsubscribe(_SHOW, oMenuItem._createKeyListener, keyData);
7945         
7946     },
7947
7948
7949     /**
7950     * @method configKeyListener
7951     * @description Event handler for when the "keylistener" configuration 
7952     * property of a menu item changes.
7953     * @param {String} p_sType String representing the name of the event that 
7954     * was fired.
7955     * @param {Array} p_aArgs Array of arguments sent when the event was fired.
7956     */
7957     configKeyListener: function (p_sType, p_aArgs) {
7958
7959         var oKeyData = p_aArgs[0],
7960             oMenuItem = this,
7961             oMenu = oMenuItem.parent;
7962
7963         if (oMenuItem._keyData) {
7964
7965             //  Unsubscribe from the "show" event in case the keylistener 
7966             //  config was changed before the Menu was ever made visible.
7967
7968             oMenu.unsubscribe(_SHOW, 
7969                     oMenuItem._createKeyListener, oMenuItem._keyData);
7970
7971             oMenuItem._keyData = null;                                  
7972                     
7973         }
7974
7975
7976         //      Tear down for the previous value of the "keylistener" property
7977
7978         if (oMenuItem._keyListener) {
7979
7980             oMenu.unsubscribe(_SHOW, oMenuItem._keyListener.enable);
7981             oMenu.unsubscribe(_HIDE, oMenuItem._keyListener.disable);
7982
7983             oMenuItem._keyListener.disable();
7984             oMenuItem._keyListener = null;
7985
7986         }
7987
7988
7989         if (oKeyData) {
7990     
7991             oMenuItem._keyData = oKeyData;
7992
7993             //  Defer the creation of the KeyListener instance until the 
7994             //  parent Menu is visible.  This is necessary since the 
7995             //  KeyListener instance needs to be bound to the document the 
7996             //  Menu has been rendered into.  Deferring creation of the 
7997             //  KeyListener instance also improves performance.
7998
7999             oMenu.subscribe(_SHOW, oMenuItem._createKeyListener, 
8000                 oKeyData, oMenuItem);
8001         }
8002     
8003     },
8004
8005
8006     // Public methods
8007
8008
8009     /**
8010     * @method initDefaultConfig
8011     * @description Initializes an item's configurable properties.
8012     */
8013     initDefaultConfig : function () {
8014
8015         var oConfig = this.cfg;
8016
8017
8018         // Define the configuration attributes
8019
8020         /**
8021         * @config text
8022         * @description String or markup specifying the text label for the menu item.  
8023         * When building a menu from existing HTML the value of this property
8024         * 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.
8025         * @default ""
8026         * @type HTML
8027         */
8028         oConfig.addProperty(
8029             TEXT_CONFIG.key, 
8030             { 
8031                 handler: this.configText, 
8032                 value: TEXT_CONFIG.value, 
8033                 validator: TEXT_CONFIG.validator, 
8034                 suppressEvent: TEXT_CONFIG.suppressEvent 
8035             }
8036         );
8037         
8038
8039         /**
8040         * @config helptext
8041         * @description String or markup specifying additional instructional text to 
8042         * 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.
8043         * @deprecated Use "text" configuration property to add help text markup.  
8044         * For example: <code>oMenuItem.cfg.setProperty("text", "Copy &#60;em 
8045         * class=\"helptext\"&#62;Ctrl + C&#60;/em&#62;");</code>
8046         * @default null
8047         * @type HTML|<a href="http://www.w3.org/TR/
8048         * 2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
8049         * HTMLElement</a>
8050         */
8051         oConfig.addProperty(
8052             HELP_TEXT_CONFIG.key,
8053             {
8054                 handler: this.configHelpText, 
8055                 supercedes: HELP_TEXT_CONFIG.supercedes,
8056                 suppressEvent: HELP_TEXT_CONFIG.suppressEvent 
8057             }
8058         );
8059
8060
8061         /**
8062         * @config url
8063         * @description String specifying the URL for the menu item's anchor's 
8064         * "href" attribute.  When building a menu from existing HTML the value 
8065         * 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.
8066         * @default "#"
8067         * @type String
8068         */        
8069         oConfig.addProperty(
8070             URL_CONFIG.key, 
8071             {
8072                 handler: this.configURL, 
8073                 value: URL_CONFIG.value, 
8074                 suppressEvent: URL_CONFIG.suppressEvent
8075             }
8076         );
8077
8078
8079         /**
8080         * @config target
8081         * @description String specifying the value for the "target" attribute 
8082         * of the menu item's anchor element. <strong>Specifying a target will 
8083         * require the user to click directly on the menu item's anchor node in
8084         * order to cause the browser to navigate to the specified URL.</strong> 
8085         * When building a menu from existing HTML the value of this property 
8086         * 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.
8087         * @default null
8088         * @type String
8089         */        
8090         oConfig.addProperty(
8091             TARGET_CONFIG.key, 
8092             {
8093                 handler: this.configTarget, 
8094                 suppressEvent: TARGET_CONFIG.suppressEvent
8095             }
8096         );
8097
8098
8099         /**
8100         * @config emphasis
8101         * @description Boolean indicating if the text of the menu item will be 
8102         * rendered with emphasis.
8103         * @deprecated Use the "text" configuration property to add emphasis.  
8104         * For example: <code>oMenuItem.cfg.setProperty("text", "&#60;em&#62;Some 
8105         * Text&#60;/em&#62;");</code>
8106         * @default false
8107         * @type Boolean
8108         */
8109         oConfig.addProperty(
8110             EMPHASIS_CONFIG.key, 
8111             { 
8112                 handler: this.configEmphasis, 
8113                 value: EMPHASIS_CONFIG.value, 
8114                 validator: EMPHASIS_CONFIG.validator, 
8115                 suppressEvent: EMPHASIS_CONFIG.suppressEvent,
8116                 supercedes: EMPHASIS_CONFIG.supercedes
8117             }
8118         );
8119
8120
8121         /**
8122         * @config strongemphasis
8123         * @description Boolean indicating if the text of the menu item will be 
8124         * rendered with strong emphasis.
8125         * @deprecated Use the "text" configuration property to add strong emphasis.  
8126         * For example: <code>oMenuItem.cfg.setProperty("text", "&#60;strong&#62; 
8127         * Some Text&#60;/strong&#62;");</code>
8128         * @default false
8129         * @type Boolean
8130         */
8131         oConfig.addProperty(
8132             STRONG_EMPHASIS_CONFIG.key,
8133             {
8134                 handler: this.configStrongEmphasis,
8135                 value: STRONG_EMPHASIS_CONFIG.value,
8136                 validator: STRONG_EMPHASIS_CONFIG.validator,
8137                 suppressEvent: STRONG_EMPHASIS_CONFIG.suppressEvent,
8138                 supercedes: STRONG_EMPHASIS_CONFIG.supercedes
8139             }
8140         );
8141
8142
8143         /**
8144         * @config checked
8145         * @description Boolean indicating if the menu item should be rendered 
8146         * with a checkmark.
8147         * @default false
8148         * @type Boolean
8149         */
8150         oConfig.addProperty(
8151             CHECKED_CONFIG.key, 
8152             {
8153                 handler: this.configChecked, 
8154                 value: CHECKED_CONFIG.value, 
8155                 validator: CHECKED_CONFIG.validator, 
8156                 suppressEvent: CHECKED_CONFIG.suppressEvent,
8157                 supercedes: CHECKED_CONFIG.supercedes
8158             } 
8159         );
8160
8161
8162         /**
8163         * @config disabled
8164         * @description Boolean indicating if the menu item should be disabled.  
8165         * (Disabled menu items are  dimmed and will not respond to user input 
8166         * or fire events.)
8167         * @default false
8168         * @type Boolean
8169         */
8170         oConfig.addProperty(
8171             DISABLED_CONFIG.key,
8172             {
8173                 handler: this.configDisabled,
8174                 value: DISABLED_CONFIG.value,
8175                 validator: DISABLED_CONFIG.validator,
8176                 suppressEvent: DISABLED_CONFIG.suppressEvent
8177             }
8178         );
8179
8180
8181         /**
8182         * @config selected
8183         * @description Boolean indicating if the menu item should 
8184         * be highlighted.
8185         * @default false
8186         * @type Boolean
8187         */
8188         oConfig.addProperty(
8189             SELECTED_CONFIG.key,
8190             {
8191                 handler: this.configSelected,
8192                 value: SELECTED_CONFIG.value,
8193                 validator: SELECTED_CONFIG.validator,
8194                 suppressEvent: SELECTED_CONFIG.suppressEvent
8195             }
8196         );
8197
8198
8199         /**
8200         * @config submenu
8201         * @description Object specifying the submenu to be appended to the 
8202         * menu item.  The value can be one of the following: <ul><li>Object 
8203         * specifying a Menu instance.</li><li>Object literal specifying the
8204         * menu to be created.  Format: <code>{ id: [menu id], itemdata: 
8205         * [<a href="YAHOO.widget.Menu.html#itemData">array of values for 
8206         * items</a>] }</code>.</li><li>String specifying the id attribute 
8207         * of the <code>&#60;div&#62;</code> element of the menu.</li><li>
8208         * Object specifying the <code>&#60;div&#62;</code> element of the 
8209         * menu.</li></ul>
8210         * @default null
8211         * @type Menu|String|Object|<a href="http://www.w3.org/TR/2000/
8212         * WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
8213         * HTMLElement</a>
8214         */
8215         oConfig.addProperty(
8216             SUBMENU_CONFIG.key, 
8217             {
8218                 handler: this.configSubmenu, 
8219                 supercedes: SUBMENU_CONFIG.supercedes,
8220                 suppressEvent: SUBMENU_CONFIG.suppressEvent
8221             }
8222         );
8223
8224
8225         /**
8226         * @config onclick
8227         * @description Object literal representing the code to be executed when 
8228         * the item is clicked.  Format:<br> <code> {<br> 
8229         * <strong>fn:</strong> Function,   &#47;&#47; The handler to call when 
8230         * the event fires.<br> <strong>obj:</strong> Object, &#47;&#47; An 
8231         * object to  pass back to the handler.<br> <strong>scope:</strong> 
8232         * Object &#47;&#47; The object to use for the scope of the handler.
8233         * <br> } </code>
8234         * @type Object
8235         * @default null
8236         */
8237         oConfig.addProperty(
8238             ONCLICK_CONFIG.key, 
8239             {
8240                 handler: this.configOnClick, 
8241                 suppressEvent: ONCLICK_CONFIG.suppressEvent 
8242             }
8243         );
8244
8245
8246         /**
8247         * @config classname
8248         * @description CSS class to be applied to the menu item's root 
8249         * <code>&#60;li&#62;</code> element.  The specified class(es) are 
8250         * appended in addition to the default class as specified by the menu 
8251         * item's CSS_CLASS_NAME constant.
8252         * @default null
8253         * @type String
8254         */
8255         oConfig.addProperty(
8256             CLASS_NAME_CONFIG.key, 
8257             { 
8258                 handler: this.configClassName,
8259                 value: CLASS_NAME_CONFIG.value, 
8260                 validator: CLASS_NAME_CONFIG.validator,
8261                 suppressEvent: CLASS_NAME_CONFIG.suppressEvent 
8262             }
8263         );
8264
8265
8266         /**
8267         * @config keylistener
8268         * @description Object literal representing the key(s) that can be used 
8269         * to trigger the MenuItem's "click" event.  Possible attributes are 
8270         * shift (boolean), alt (boolean), ctrl (boolean) and keys (either an int 
8271         * or an array of ints representing keycodes).
8272         * @default null
8273         * @type Object
8274         */
8275         oConfig.addProperty(
8276             KEY_LISTENER_CONFIG.key, 
8277             { 
8278                 handler: this.configKeyListener,
8279                 value: KEY_LISTENER_CONFIG.value, 
8280                 suppressEvent: KEY_LISTENER_CONFIG.suppressEvent 
8281             }
8282         );
8283
8284     },
8285
8286     /**
8287     * @method getNextSibling
8288     * @description Finds the menu item's next sibling.
8289     * @return YAHOO.widget.MenuItem
8290     */
8291     getNextSibling: function () {
8292     
8293         var isUL = function (el) {
8294                 return (el.nodeName.toLowerCase() === "ul");
8295             },
8296     
8297             menuitemEl = this.element,
8298             next = Dom.getNextSibling(menuitemEl),
8299             parent,
8300             sibling,
8301             list;
8302         
8303         if (!next) {
8304             
8305             parent = menuitemEl.parentNode;
8306             sibling = Dom.getNextSiblingBy(parent, isUL);
8307             
8308             if (sibling) {
8309                 list = sibling;
8310             }
8311             else {
8312                 list = Dom.getFirstChildBy(parent.parentNode, isUL);
8313             }
8314             
8315             next = Dom.getFirstChild(list);
8316             
8317         }
8318
8319         return YAHOO.widget.MenuManager.getMenuItem(next.id);
8320
8321     },
8322
8323     /**
8324     * @method getNextEnabledSibling
8325     * @description Finds the menu item's next enabled sibling.
8326     * @return YAHOO.widget.MenuItem
8327     */
8328     getNextEnabledSibling: function () {
8329         
8330         var next = this.getNextSibling();
8331         
8332         return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getNextEnabledSibling() : next;
8333         
8334     },
8335
8336
8337     /**
8338     * @method getPreviousSibling
8339     * @description Finds the menu item's previous sibling.
8340     * @return {YAHOO.widget.MenuItem}
8341     */  
8342     getPreviousSibling: function () {
8343
8344         var isUL = function (el) {
8345                 return (el.nodeName.toLowerCase() === "ul");
8346             },
8347
8348             menuitemEl = this.element,
8349             next = Dom.getPreviousSibling(menuitemEl),
8350             parent,
8351             sibling,
8352             list;
8353         
8354         if (!next) {
8355             
8356             parent = menuitemEl.parentNode;
8357             sibling = Dom.getPreviousSiblingBy(parent, isUL);
8358             
8359             if (sibling) {
8360                 list = sibling;
8361             }
8362             else {
8363                 list = Dom.getLastChildBy(parent.parentNode, isUL);
8364             }
8365             
8366             next = Dom.getLastChild(list);
8367             
8368         }
8369
8370         return YAHOO.widget.MenuManager.getMenuItem(next.id);
8371         
8372     },
8373
8374
8375     /**
8376     * @method getPreviousEnabledSibling
8377     * @description Finds the menu item's previous enabled sibling.
8378     * @return {YAHOO.widget.MenuItem}
8379     */
8380     getPreviousEnabledSibling: function () {
8381         
8382         var next = this.getPreviousSibling();
8383         
8384         return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getPreviousEnabledSibling() : next;
8385         
8386     },
8387
8388
8389     /**
8390     * @method focus
8391     * @description Causes the menu item to receive the focus and fires the 
8392     * focus event.
8393     */
8394     focus: function () {
8395
8396         var oParent = this.parent,
8397             oAnchor = this._oAnchor,
8398             oActiveItem = oParent.activeItem;
8399
8400
8401         function setFocus() {
8402
8403             try {
8404
8405                 if (!(UA.ie && !document.hasFocus())) {
8406                 
8407                     if (oActiveItem) {
8408         
8409                         oActiveItem.blurEvent.fire();
8410         
8411                     }
8412     
8413                     oAnchor.focus();
8414                     
8415                     this.focusEvent.fire();
8416                 
8417                 }
8418
8419             }
8420             catch(e) {
8421             
8422             }
8423
8424         }
8425
8426
8427         if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE) && 
8428             this.element.style.display != _NONE) {
8429
8430
8431             /*
8432                 Setting focus via a timer fixes a race condition in Firefox, IE 
8433                 and Opera where the browser viewport jumps as it trys to 
8434                 position and focus the menu.
8435             */
8436
8437             Lang.later(0, this, setFocus);
8438
8439         }
8440
8441     },
8442
8443
8444     /**
8445     * @method blur
8446     * @description Causes the menu item to lose focus and fires the 
8447     * blur event.
8448     */    
8449     blur: function () {
8450
8451         var oParent = this.parent;
8452
8453         if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE)) {
8454
8455             Lang.later(0, this, function () {
8456
8457                 try {
8458     
8459                     this._oAnchor.blur();
8460                     this.blurEvent.fire();    
8461
8462                 } 
8463                 catch (e) {
8464                 
8465                 }
8466                 
8467             }, 0);
8468
8469         }
8470
8471     },
8472
8473
8474     /**
8475     * @method hasFocus
8476     * @description Returns a boolean indicating whether or not the menu item
8477     * has focus.
8478     * @return {Boolean}
8479     */
8480     hasFocus: function () {
8481     
8482         return (YAHOO.widget.MenuManager.getFocusedMenuItem() == this);
8483     
8484     },
8485
8486
8487     /**
8488     * @method destroy
8489     * @description Removes the menu item's <code>&#60;li&#62;</code> element 
8490     * from its parent <code>&#60;ul&#62;</code> element.
8491     */
8492     destroy: function () {
8493
8494         var oEl = this.element,
8495             oSubmenu,
8496             oParentNode,
8497             aEventData,
8498             i;
8499
8500
8501         if (oEl) {
8502
8503
8504             // If the item has a submenu, destroy it first
8505
8506             oSubmenu = this.cfg.getProperty(_SUBMENU);
8507
8508             if (oSubmenu) {
8509             
8510                 oSubmenu.destroy();
8511             
8512             }
8513
8514
8515             // Remove the element from the parent node
8516
8517             oParentNode = oEl.parentNode;
8518
8519             if (oParentNode) {
8520
8521                 oParentNode.removeChild(oEl);
8522
8523                 this.destroyEvent.fire();
8524
8525             }
8526
8527
8528             // Remove CustomEvent listeners
8529
8530             i = EVENT_TYPES.length - 1;
8531
8532             do {
8533
8534                 aEventData = EVENT_TYPES[i];
8535                 
8536                 this[aEventData[0]].unsubscribeAll();
8537
8538             }
8539             while (i--);
8540             
8541             
8542             this.cfg.configChangedEvent.unsubscribeAll();
8543
8544         }
8545
8546     },
8547
8548
8549     /**
8550     * @method toString
8551     * @description Returns a string representing the menu item.
8552     * @return {String}
8553     */
8554     toString: function () {
8555
8556         var sReturnVal = _MENUITEM,
8557             sId = this.id;
8558
8559         if (sId) {
8560     
8561             sReturnVal += (_SPACE + sId);
8562         
8563         }
8564
8565         return sReturnVal;
8566     
8567     }
8568
8569 };
8570
8571 Lang.augmentProto(MenuItem, YAHOO.util.EventProvider);
8572
8573 })();
8574 (function () {
8575
8576     var _XY = "xy",
8577         _MOUSEDOWN = "mousedown",
8578         _CONTEXTMENU = "ContextMenu",
8579         _SPACE = " ";
8580
8581 /**
8582 * Creates a list of options or commands which are made visible in response to 
8583 * an HTML element's "contextmenu" event ("mousedown" for Opera).
8584 *
8585 * @param {String} p_oElement String specifying the id attribute of the 
8586 * <code>&#60;div&#62;</code> element of the context menu.
8587 * @param {String} p_oElement String specifying the id attribute of the 
8588 * <code>&#60;select&#62;</code> element to be used as the data source for the 
8589 * context menu.
8590 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8591 * html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 
8592 * <code>&#60;div&#62;</code> element of the context menu.
8593 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8594 * html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 
8595 * the <code>&#60;select&#62;</code> element to be used as the data source for 
8596 * the context menu.
8597 * @param {Object} p_oConfig Optional. Object literal specifying the 
8598 * configuration for the context menu. See configuration class documentation 
8599 * for more details.
8600 * @class ContextMenu
8601 * @constructor
8602 * @extends YAHOO.widget.Menu
8603 * @namespace YAHOO.widget
8604 */
8605 YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) {
8606     YAHOO.widget.ContextMenu.superclass.constructor.call(this, p_oElement, p_oConfig);
8607 };
8608
8609
8610 var Event = YAHOO.util.Event,
8611     UA = YAHOO.env.ua,
8612     ContextMenu = YAHOO.widget.ContextMenu,
8613
8614
8615
8616     /**
8617     * Constant representing the name of the ContextMenu's events
8618     * @property EVENT_TYPES
8619     * @private
8620     * @final
8621     * @type Object
8622     */
8623     EVENT_TYPES = {
8624
8625         "TRIGGER_CONTEXT_MENU": "triggerContextMenu",
8626         "CONTEXT_MENU": (UA.opera ? _MOUSEDOWN : "contextmenu"),
8627         "CLICK": "click"
8628
8629     },
8630     
8631     
8632     /**
8633     * Constant representing the ContextMenu's configuration properties
8634     * @property DEFAULT_CONFIG
8635     * @private
8636     * @final
8637     * @type Object
8638     */
8639     TRIGGER_CONFIG = { 
8640         key: "trigger",
8641         suppressEvent: true
8642     };
8643
8644
8645 /**
8646 * @method position
8647 * @description "beforeShow" event handler used to position the contextmenu.
8648 * @private
8649 * @param {String} p_sType String representing the name of the event that 
8650 * was fired.
8651 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
8652 * @param {Array} p_aPos Array representing the xy position for the context menu.
8653 */
8654 function position(p_sType, p_aArgs, p_aPos) {
8655     this.cfg.setProperty(_XY, p_aPos);
8656     this.beforeShowEvent.unsubscribe(position, p_aPos);
8657 }
8658
8659
8660 YAHOO.lang.extend(ContextMenu, YAHOO.widget.Menu, {
8661
8662
8663
8664 // Private properties
8665
8666
8667 /**
8668 * @property _oTrigger
8669 * @description Object reference to the current value of the "trigger" 
8670 * configuration property.
8671 * @default null
8672 * @private
8673 * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/leve
8674 * l-one-html.html#ID-58190037">HTMLElement</a>|Array
8675 */
8676 _oTrigger: null,
8677
8678
8679 /**
8680 * @property _bCancelled
8681 * @description Boolean indicating if the display of the context menu should 
8682 * be cancelled.
8683 * @default false
8684 * @private
8685 * @type Boolean
8686 */
8687 _bCancelled: false,
8688
8689
8690
8691 // Public properties
8692
8693
8694 /**
8695 * @property contextEventTarget
8696 * @description Object reference for the HTML element that was the target of the
8697 * "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of 
8698 * the context menu.
8699 * @default null
8700 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8701 * html.html#ID-58190037">HTMLElement</a>
8702 */
8703 contextEventTarget: null,
8704
8705
8706
8707 // Events
8708
8709
8710 /**
8711 * @event triggerContextMenuEvent
8712 * @param type {String} The name of the event, "triggerContextMenu"
8713 * @param args {Array} The array of event arguments. For this event, the underlying
8714 * DOM event is the only argument, available from args[0].
8715 * @description Custom Event wrapper for the "contextmenu" DOM event 
8716 * ("mousedown" for Opera) fired by the element(s) that trigger the display of 
8717 * the context menu.
8718 */
8719 triggerContextMenuEvent: null,
8720
8721
8722
8723 /**
8724 * @method init
8725 * @description The ContextMenu class's initialization method. This method is 
8726 * automatically called by the constructor, and sets up all DOM references for 
8727 * pre-existing markup, and creates required markup if it is not already present.
8728 * @param {String} p_oElement String specifying the id attribute of the 
8729 * <code>&#60;div&#62;</code> element of the context menu.
8730 * @param {String} p_oElement String specifying the id attribute of the 
8731 * <code>&#60;select&#62;</code> element to be used as the data source for 
8732 * the context menu.
8733 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8734 * html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 
8735 * <code>&#60;div&#62;</code> element of the context menu.
8736 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
8737 * html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 
8738 * the <code>&#60;select&#62;</code> element to be used as the data source for 
8739 * the context menu.
8740 * @param {Object} p_oConfig Optional. Object literal specifying the 
8741 * configuration for the context menu. See configuration class documentation 
8742 * for more details.
8743 */
8744 init: function(p_oElement, p_oConfig) {
8745
8746
8747     // Call the init of the superclass (YAHOO.widget.Menu)
8748     
8749     ContextMenu.superclass.init.call(this, p_oElement);
8750
8751     this.beforeInitEvent.fire(ContextMenu);
8752
8753     if (p_oConfig) {
8754         this.cfg.applyConfig(p_oConfig, true);
8755     }
8756
8757     this.initEvent.fire(ContextMenu);
8758 },
8759
8760
8761 /**
8762 * @method initEvents
8763 * @description Initializes the custom events for the context menu.
8764 */
8765 initEvents: function() {
8766     ContextMenu.superclass.initEvents.call(this);
8767
8768     // Create custom events
8769     this.triggerContextMenuEvent = this.createEvent(EVENT_TYPES.TRIGGER_CONTEXT_MENU);
8770     this.triggerContextMenuEvent.signature = YAHOO.util.CustomEvent.LIST;
8771 },
8772
8773 /**
8774 * @method cancel
8775 * @description Cancels the display of the context menu.
8776 */
8777 cancel: function() {
8778     this._bCancelled = true;
8779 },
8780
8781 // Private methods
8782
8783
8784 /**
8785 * @method _removeEventHandlers
8786 * @description Removes all of the DOM event handlers from the HTML element(s) 
8787 * whose "context menu" event ("click" for Opera) trigger the display of 
8788 * the context menu.
8789 * @private
8790 */
8791 _removeEventHandlers: function() {
8792
8793     var oTrigger = this._oTrigger;
8794
8795     // Remove the event handlers from the trigger(s)
8796     if (oTrigger) {
8797         Event.removeListener(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu);    
8798
8799         if (UA.opera) {
8800             Event.removeListener(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick);
8801         }
8802     }
8803
8804 },
8805
8806 // Private event handlers
8807
8808 /**
8809 * @method _onTriggerClick
8810 * @description "click" event handler for the HTML element(s) identified as the 
8811 * "trigger" for the context menu.  Used to cancel default behaviors in Opera.
8812 * @private
8813 * @param {Event} p_oEvent Object representing the DOM event object passed back 
8814 * by the event utility (YAHOO.util.Event).
8815 * @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
8816 * menu that is handling the event.
8817 */
8818 _onTriggerClick: function(p_oEvent, p_oMenu) {
8819
8820     if (p_oEvent.ctrlKey) {
8821         Event.stopEvent(p_oEvent);
8822     }
8823     
8824 },
8825
8826
8827 /**
8828 * @method _onTriggerContextMenu
8829 * @description "contextmenu" event handler ("mousedown" for Opera) for the HTML 
8830 * element(s) that trigger the display of the context menu.
8831 * @private
8832 * @param {Event} p_oEvent Object representing the DOM event object passed back 
8833 * by the event utility (YAHOO.util.Event).
8834 * @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
8835 * menu that is handling the event.
8836 */
8837 _onTriggerContextMenu: function(p_oEvent, p_oMenu) {
8838
8839     var aXY;
8840
8841     if (!(p_oEvent.type == _MOUSEDOWN && !p_oEvent.ctrlKey)) {
8842     
8843         this.contextEventTarget = Event.getTarget(p_oEvent);
8844     
8845         this.triggerContextMenuEvent.fire(p_oEvent);
8846         
8847     
8848         if (!this._bCancelled) {
8849
8850             /*
8851                 Prevent the browser's default context menu from appearing and 
8852                 stop the propagation of the "contextmenu" event so that 
8853                 other ContextMenu instances are not displayed.
8854             */
8855
8856             Event.stopEvent(p_oEvent);
8857
8858
8859             // Hide any other Menu instances that might be visible
8860
8861             YAHOO.widget.MenuManager.hideVisible();
8862             
8863     
8864
8865             // Position and display the context menu
8866     
8867             aXY = Event.getXY(p_oEvent);
8868     
8869     
8870             if (!YAHOO.util.Dom.inDocument(this.element)) {
8871     
8872                 this.beforeShowEvent.subscribe(position, aXY);
8873     
8874             }
8875             else {
8876     
8877                 this.cfg.setProperty(_XY, aXY);
8878             
8879             }
8880     
8881     
8882             this.show();
8883     
8884         }
8885     
8886         this._bCancelled = false;
8887
8888     }
8889
8890 },
8891
8892
8893
8894 // Public methods
8895
8896
8897 /**
8898 * @method toString
8899 * @description Returns a string representing the context menu.
8900 * @return {String}
8901 */
8902 toString: function() {
8903
8904     var sReturnVal = _CONTEXTMENU,
8905         sId = this.id;
8906
8907     if (sId) {
8908
8909         sReturnVal += (_SPACE + sId);
8910     
8911     }
8912
8913     return sReturnVal;
8914
8915 },
8916
8917
8918 /**
8919 * @method initDefaultConfig
8920 * @description Initializes the class's configurable properties which can be 
8921 * changed using the context menu's Config object ("cfg").
8922 */
8923 initDefaultConfig: function() {
8924
8925     ContextMenu.superclass.initDefaultConfig.call(this);
8926
8927     /**
8928     * @config trigger
8929     * @description The HTML element(s) whose "contextmenu" event ("mousedown" 
8930     * for Opera) trigger the display of the context menu.  Can be a string 
8931     * representing the id attribute of the HTML element, an object reference 
8932     * for the HTML element, or an array of strings or HTML element references.
8933     * @default null
8934     * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
8935     * level-one-html.html#ID-58190037">HTMLElement</a>|Array
8936     */
8937     this.cfg.addProperty(TRIGGER_CONFIG.key, 
8938         {
8939             handler: this.configTrigger, 
8940             suppressEvent: TRIGGER_CONFIG.suppressEvent 
8941         }
8942     );
8943
8944 },
8945
8946
8947 /**
8948 * @method destroy
8949 * @description Removes the context menu's <code>&#60;div&#62;</code> element 
8950 * (and accompanying child nodes) from the document.
8951 * @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. 
8952 * 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.
8953 */
8954 destroy: function(shallowPurge) {
8955
8956     // Remove the DOM event handlers from the current trigger(s)
8957
8958     this._removeEventHandlers();
8959
8960
8961     // Continue with the superclass implementation of this method
8962
8963     ContextMenu.superclass.destroy.call(this, shallowPurge);
8964
8965 },
8966
8967
8968
8969 // Public event handlers for configuration properties
8970
8971
8972 /**
8973 * @method configTrigger
8974 * @description Event handler for when the value of the "trigger" configuration 
8975 * property changes. 
8976 * @param {String} p_sType String representing the name of the event that 
8977 * was fired.
8978 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
8979 * @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
8980 * menu that fired the event.
8981 */
8982 configTrigger: function(p_sType, p_aArgs, p_oMenu) {
8983     
8984     var oTrigger = p_aArgs[0];
8985
8986     if (oTrigger) {
8987
8988         /*
8989             If there is a current "trigger" - remove the event handlers 
8990             from that element(s) before assigning new ones
8991         */
8992
8993         if (this._oTrigger) {
8994         
8995             this._removeEventHandlers();
8996
8997         }
8998
8999         this._oTrigger = oTrigger;
9000
9001
9002         /*
9003             Listen for the "mousedown" event in Opera b/c it does not 
9004             support the "contextmenu" event
9005         */ 
9006   
9007         Event.on(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu, this, true);
9008
9009
9010         /*
9011             Assign a "click" event handler to the trigger element(s) for
9012             Opera to prevent default browser behaviors.
9013         */
9014
9015         if (UA.opera) {
9016         
9017             Event.on(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick, this, true);
9018
9019         }
9020
9021     }
9022     else {
9023    
9024         this._removeEventHandlers();
9025     
9026     }
9027     
9028 }
9029
9030 }); // END YAHOO.lang.extend
9031
9032 }());
9033
9034
9035
9036 /**
9037 * Creates an item for a context menu.
9038
9039 * @param {String} p_oObject String specifying the text of the context menu item.
9040 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9041 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
9042 * <code>&#60;li&#62;</code> element of the context menu item.
9043 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9044 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
9045 * specifying the <code>&#60;optgroup&#62;</code> element of the context 
9046 * menu item.
9047 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9048 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
9049 * the <code>&#60;option&#62;</code> element of the context menu item.
9050 * @param {Object} p_oConfig Optional. Object literal specifying the 
9051 * configuration for the context menu item. See configuration class 
9052 * documentation for more details.
9053 * @class ContextMenuItem
9054 * @constructor
9055 * @extends YAHOO.widget.MenuItem
9056 * @deprecated As of version 2.4.0 items for YAHOO.widget.ContextMenu instances
9057 * are of type YAHOO.widget.MenuItem.
9058 */
9059 YAHOO.widget.ContextMenuItem = YAHOO.widget.MenuItem;
9060 (function () {
9061
9062     var Lang = YAHOO.lang,
9063
9064         // String constants
9065     
9066         _STATIC = "static",
9067         _DYNAMIC_STATIC = "dynamic," + _STATIC,
9068         _DISABLED = "disabled",
9069         _SELECTED = "selected",
9070         _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay",
9071         _SUBMENU = "submenu",
9072         _VISIBLE = "visible",
9073         _SPACE = " ",
9074         _SUBMENU_TOGGLE_REGION = "submenutoggleregion",
9075         _MENUBAR = "MenuBar";
9076
9077 /**
9078 * Horizontal collection of items, each of which can contain a submenu.
9079
9080 * @param {String} p_oElement String specifying the id attribute of the 
9081 * <code>&#60;div&#62;</code> element of the menu bar.
9082 * @param {String} p_oElement String specifying the id attribute of the 
9083 * <code>&#60;select&#62;</code> element to be used as the data source for the 
9084 * menu bar.
9085 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9086 * one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 
9087 * the <code>&#60;div&#62;</code> element of the menu bar.
9088 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9089 * one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 
9090 * specifying the <code>&#60;select&#62;</code> element to be used as the data 
9091 * source for the menu bar.
9092 * @param {Object} p_oConfig Optional. Object literal specifying the 
9093 * configuration for the menu bar. See configuration class documentation for
9094 * more details.
9095 * @class MenuBar
9096 * @constructor
9097 * @extends YAHOO.widget.Menu
9098 * @namespace YAHOO.widget
9099 */
9100 YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) {
9101
9102     YAHOO.widget.MenuBar.superclass.constructor.call(this, p_oElement, p_oConfig);
9103
9104 };
9105
9106
9107 /**
9108 * @method checkPosition
9109 * @description Checks to make sure that the value of the "position" property 
9110 * is one of the supported strings. Returns true if the position is supported.
9111 * @private
9112 * @param {Object} p_sPosition String specifying the position of the menu.
9113 * @return {Boolean}
9114 */
9115 function checkPosition(p_sPosition) {
9116
9117     var returnVal = false;
9118
9119     if (Lang.isString(p_sPosition)) {
9120
9121         returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1);
9122
9123     }
9124     
9125     return returnVal;
9126
9127 }
9128
9129
9130 var Event = YAHOO.util.Event,
9131     MenuBar = YAHOO.widget.MenuBar,
9132
9133     POSITION_CONFIG =  { 
9134         key: "position", 
9135         value: _STATIC, 
9136         validator: checkPosition, 
9137         supercedes: [_VISIBLE] 
9138     }, 
9139
9140     SUBMENU_ALIGNMENT_CONFIG =  { 
9141         key: "submenualignment", 
9142         value: ["tl","bl"]
9143     },
9144
9145     AUTO_SUBMENU_DISPLAY_CONFIG =  { 
9146         key: _AUTO_SUBMENU_DISPLAY, 
9147         value: false, 
9148         validator: Lang.isBoolean,
9149         suppressEvent: true
9150     },
9151     
9152     SUBMENU_TOGGLE_REGION_CONFIG = {
9153         key: _SUBMENU_TOGGLE_REGION, 
9154         value: false, 
9155         validator: Lang.isBoolean
9156     };
9157
9158
9159
9160 Lang.extend(MenuBar, YAHOO.widget.Menu, {
9161
9162 /**
9163 * @method init
9164 * @description The MenuBar class's initialization method. This method is 
9165 * automatically called by the constructor, and sets up all DOM references for 
9166 * pre-existing markup, and creates required markup if it is not already present.
9167 * @param {String} p_oElement String specifying the id attribute of the 
9168 * <code>&#60;div&#62;</code> element of the menu bar.
9169 * @param {String} p_oElement String specifying the id attribute of the 
9170 * <code>&#60;select&#62;</code> element to be used as the data source for the 
9171 * menu bar.
9172 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9173 * one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 
9174 * the <code>&#60;div&#62;</code> element of the menu bar.
9175 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9176 * one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 
9177 * specifying the <code>&#60;select&#62;</code> element to be used as the data 
9178 * source for the menu bar.
9179 * @param {Object} p_oConfig Optional. Object literal specifying the 
9180 * configuration for the menu bar. See configuration class documentation for
9181 * more details.
9182 */
9183 init: function(p_oElement, p_oConfig) {
9184
9185     if(!this.ITEM_TYPE) {
9186
9187         this.ITEM_TYPE = YAHOO.widget.MenuBarItem;
9188
9189     }
9190
9191
9192     // Call the init of the superclass (YAHOO.widget.Menu)
9193
9194     MenuBar.superclass.init.call(this, p_oElement);
9195
9196
9197     this.beforeInitEvent.fire(MenuBar);
9198
9199
9200     if(p_oConfig) {
9201
9202         this.cfg.applyConfig(p_oConfig, true);
9203
9204     }
9205
9206     this.initEvent.fire(MenuBar);
9207
9208 },
9209
9210
9211
9212 // Constants
9213
9214
9215 /**
9216 * @property CSS_CLASS_NAME
9217 * @description String representing the CSS class(es) to be applied to the menu 
9218 * bar's <code>&#60;div&#62;</code> element.
9219 * @default "yuimenubar"
9220 * @final
9221 * @type String
9222 */
9223 CSS_CLASS_NAME: "yuimenubar",
9224
9225
9226 /**
9227 * @property SUBMENU_TOGGLE_REGION_WIDTH
9228 * @description Width (in pixels) of the area of a MenuBarItem that, when pressed, will toggle the
9229 * display of the MenuBarItem's submenu.
9230 * @default 20
9231 * @final
9232 * @type Number
9233 */
9234 SUBMENU_TOGGLE_REGION_WIDTH: 20,
9235
9236
9237 // Protected event handlers
9238
9239
9240 /**
9241 * @method _onKeyDown
9242 * @description "keydown" Custom Event handler for the menu bar.
9243 * @private
9244 * @param {String} p_sType String representing the name of the event that 
9245 * was fired.
9246 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
9247 * @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 
9248 * that fired the event.
9249 */
9250 _onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) {
9251
9252     var oEvent = p_aArgs[0],
9253         oItem = p_aArgs[1],
9254         oSubmenu,
9255         oItemCfg,
9256         oNextItem;
9257
9258
9259     if(oItem && !oItem.cfg.getProperty(_DISABLED)) {
9260
9261         oItemCfg = oItem.cfg;
9262
9263         switch(oEvent.keyCode) {
9264     
9265             case 37:    // Left arrow
9266             case 39:    // Right arrow
9267     
9268                 if(oItem == this.activeItem && !oItemCfg.getProperty(_SELECTED)) {
9269     
9270                     oItemCfg.setProperty(_SELECTED, true);
9271     
9272                 }
9273                 else {
9274     
9275                     oNextItem = (oEvent.keyCode == 37) ? 
9276                         oItem.getPreviousEnabledSibling() : 
9277                         oItem.getNextEnabledSibling();
9278             
9279                     if(oNextItem) {
9280     
9281                         this.clearActiveItem();
9282     
9283                         oNextItem.cfg.setProperty(_SELECTED, true);
9284                         
9285                         oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
9286                         
9287                         if(oSubmenu) {
9288                     
9289                             oSubmenu.show();
9290                             oSubmenu.setInitialFocus();
9291                         
9292                         }
9293                         else {
9294                             oNextItem.focus();  
9295                         }
9296     
9297                     }
9298     
9299                 }
9300     
9301                 Event.preventDefault(oEvent);
9302     
9303             break;
9304     
9305             case 40:    // Down arrow
9306     
9307                 if(this.activeItem != oItem) {
9308     
9309                     this.clearActiveItem();
9310     
9311                     oItemCfg.setProperty(_SELECTED, true);
9312                     oItem.focus();
9313                 
9314                 }
9315     
9316                 oSubmenu = oItemCfg.getProperty(_SUBMENU);
9317     
9318                 if(oSubmenu) {
9319     
9320                     if(oSubmenu.cfg.getProperty(_VISIBLE)) {
9321     
9322                         oSubmenu.setInitialSelection();
9323                         oSubmenu.setInitialFocus();
9324                     
9325                     }
9326                     else {
9327     
9328                         oSubmenu.show();
9329                         oSubmenu.setInitialFocus();
9330                     
9331                     }
9332     
9333                 }
9334     
9335                 Event.preventDefault(oEvent);
9336     
9337             break;
9338     
9339         }
9340
9341     }
9342
9343
9344     if(oEvent.keyCode == 27 && this.activeItem) { // Esc key
9345
9346         oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU);
9347
9348         if(oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) {
9349         
9350             oSubmenu.hide();
9351             this.activeItem.focus();
9352         
9353         }
9354         else {
9355
9356             this.activeItem.cfg.setProperty(_SELECTED, false);
9357             this.activeItem.blur();
9358     
9359         }
9360
9361         Event.preventDefault(oEvent);
9362     
9363     }
9364
9365 },
9366
9367
9368 /**
9369 * @method _onClick
9370 * @description "click" event handler for the menu bar.
9371 * @protected
9372 * @param {String} p_sType String representing the name of the event that 
9373 * was fired.
9374 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
9375 * @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 
9376 * that fired the event.
9377 */
9378 _onClick: function(p_sType, p_aArgs, p_oMenuBar) {
9379
9380     MenuBar.superclass._onClick.call(this, p_sType, p_aArgs, p_oMenuBar);
9381
9382     var oItem = p_aArgs[1],
9383         bReturnVal = true,
9384         oItemEl,
9385         oEvent,
9386         oTarget,
9387         oActiveItem,
9388         oConfig,
9389         oSubmenu,
9390         nMenuItemX,
9391         nToggleRegion;
9392
9393
9394     var toggleSubmenuDisplay = function () {
9395
9396         if(oSubmenu.cfg.getProperty(_VISIBLE)) {
9397         
9398             oSubmenu.hide();
9399         
9400         }
9401         else {
9402         
9403             oSubmenu.show();                    
9404         
9405         }
9406     
9407     };
9408     
9409
9410     if(oItem && !oItem.cfg.getProperty(_DISABLED)) {
9411
9412         oEvent = p_aArgs[0];
9413         oTarget = Event.getTarget(oEvent);
9414         oActiveItem = this.activeItem;
9415         oConfig = this.cfg;
9416
9417
9418         // Hide any other submenus that might be visible
9419     
9420         if(oActiveItem && oActiveItem != oItem) {
9421     
9422             this.clearActiveItem();
9423     
9424         }
9425
9426     
9427         oItem.cfg.setProperty(_SELECTED, true);
9428     
9429
9430         // Show the submenu for the item
9431     
9432         oSubmenu = oItem.cfg.getProperty(_SUBMENU);
9433
9434
9435         if(oSubmenu) {
9436
9437             oItemEl = oItem.element;
9438             nMenuItemX = YAHOO.util.Dom.getX(oItemEl);
9439             nToggleRegion = nMenuItemX + (oItemEl.offsetWidth - this.SUBMENU_TOGGLE_REGION_WIDTH);
9440
9441             if (oConfig.getProperty(_SUBMENU_TOGGLE_REGION)) {
9442
9443                 if (Event.getPageX(oEvent) > nToggleRegion) {
9444
9445                     toggleSubmenuDisplay();
9446
9447                     Event.preventDefault(oEvent);
9448
9449                     /*
9450                          Return false so that other click event handlers are not called when the 
9451                          user clicks inside the toggle region.
9452                     */
9453                     bReturnVal = false;
9454                 
9455                 }
9456         
9457             }
9458             else {
9459
9460                 toggleSubmenuDisplay();
9461             
9462             }
9463         
9464         }
9465     
9466     }
9467
9468
9469     return bReturnVal;
9470
9471 },
9472
9473
9474
9475 // Public methods
9476
9477 /**
9478 * @method configSubmenuToggle
9479 * @description Event handler for when the "submenutoggleregion" configuration property of 
9480 * a MenuBar changes.
9481 * @param {String} p_sType The name of the event that was fired.
9482 * @param {Array} p_aArgs Collection of arguments sent when the event was fired.
9483 */
9484 configSubmenuToggle: function (p_sType, p_aArgs) {
9485
9486     var bSubmenuToggle = p_aArgs[0];
9487     
9488     if (bSubmenuToggle) {
9489     
9490         this.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false);
9491     
9492     }
9493
9494 },
9495
9496
9497 /**
9498 * @method toString
9499 * @description Returns a string representing the menu bar.
9500 * @return {String}
9501 */
9502 toString: function() {
9503
9504     var sReturnVal = _MENUBAR,
9505         sId = this.id;
9506
9507     if(sId) {
9508
9509         sReturnVal += (_SPACE + sId);
9510     
9511     }
9512
9513     return sReturnVal;
9514
9515 },
9516
9517
9518 /**
9519 * @description Initializes the class's configurable properties which can be
9520 * changed using the menu bar's Config object ("cfg").
9521 * @method initDefaultConfig
9522 */
9523 initDefaultConfig: function() {
9524
9525     MenuBar.superclass.initDefaultConfig.call(this);
9526
9527     var oConfig = this.cfg;
9528
9529     // Add configuration properties
9530
9531
9532     /*
9533         Set the default value for the "position" configuration property
9534         to "static" by re-adding the property.
9535     */
9536
9537
9538     /**
9539     * @config position
9540     * @description String indicating how a menu bar should be positioned on the 
9541     * screen.  Possible values are "static" and "dynamic."  Static menu bars 
9542     * are visible by default and reside in the normal flow of the document 
9543     * (CSS position: static).  Dynamic menu bars are hidden by default, reside
9544     * out of the normal flow of the document (CSS position: absolute), and can 
9545     * overlay other elements on the screen.
9546     * @default static
9547     * @type String
9548     */
9549     oConfig.addProperty(
9550         POSITION_CONFIG.key, 
9551         {
9552             handler: this.configPosition, 
9553             value: POSITION_CONFIG.value, 
9554             validator: POSITION_CONFIG.validator,
9555             supercedes: POSITION_CONFIG.supercedes
9556         }
9557     );
9558
9559
9560     /*
9561         Set the default value for the "submenualignment" configuration property
9562         to ["tl","bl"] by re-adding the property.
9563     */
9564
9565     /**
9566     * @config submenualignment
9567     * @description Array defining how submenus should be aligned to their 
9568     * parent menu bar item. The format is: [itemCorner, submenuCorner].
9569     * @default ["tl","bl"]
9570     * @type Array
9571     */
9572     oConfig.addProperty(
9573         SUBMENU_ALIGNMENT_CONFIG.key, 
9574         {
9575             value: SUBMENU_ALIGNMENT_CONFIG.value,
9576             suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent
9577         }
9578     );
9579
9580
9581     /*
9582         Change the default value for the "autosubmenudisplay" configuration 
9583         property to "false" by re-adding the property.
9584     */
9585
9586     /**
9587     * @config autosubmenudisplay
9588     * @description Boolean indicating if submenus are automatically made 
9589     * visible when the user mouses over the menu bar's items.
9590     * @default false
9591     * @type Boolean
9592     */
9593     oConfig.addProperty(
9594        AUTO_SUBMENU_DISPLAY_CONFIG.key, 
9595        {
9596            value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 
9597            validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator,
9598            suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent
9599        } 
9600     );
9601
9602
9603     /**
9604     * @config submenutoggleregion
9605     * @description Boolean indicating if only a specific region of a MenuBarItem should toggle the 
9606     * display of a submenu.  The default width of the region is determined by the value of the
9607     * SUBMENU_TOGGLE_REGION_WIDTH property.  If set to true, the autosubmenudisplay 
9608     * configuration property will be set to false, and any click event listeners will not be 
9609     * called when the user clicks inside the submenu toggle region of a MenuBarItem.  If the 
9610     * user clicks outside of the submenu toggle region, the MenuBarItem will maintain its 
9611     * standard behavior.
9612     * @default false
9613     * @type Boolean
9614     */
9615     oConfig.addProperty(
9616        SUBMENU_TOGGLE_REGION_CONFIG.key, 
9617        {
9618            value: SUBMENU_TOGGLE_REGION_CONFIG.value, 
9619            validator: SUBMENU_TOGGLE_REGION_CONFIG.validator,
9620            handler: this.configSubmenuToggle
9621        } 
9622     );
9623
9624 }
9625  
9626 }); // END YAHOO.lang.extend
9627
9628 }());
9629
9630
9631
9632 /**
9633 * Creates an item for a menu bar.
9634
9635 * @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.
9636 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9637 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
9638 * <code>&#60;li&#62;</code> element of the menu bar item.
9639 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9640 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
9641 * specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
9642 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9643 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
9644 * the <code>&#60;option&#62;</code> element of the menu bar item.
9645 * @param {Object} p_oConfig Optional. Object literal specifying the 
9646 * configuration for the menu bar item. See configuration class documentation 
9647 * for more details.
9648 * @class MenuBarItem
9649 * @constructor
9650 * @extends YAHOO.widget.MenuItem
9651 */
9652 YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) {
9653
9654     YAHOO.widget.MenuBarItem.superclass.constructor.call(this, p_oObject, p_oConfig);
9655
9656 };
9657
9658 YAHOO.lang.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, {
9659
9660
9661
9662 /**
9663 * @method init
9664 * @description The MenuBarItem class's initialization method. This method is 
9665 * automatically called by the constructor, and sets up all DOM references for 
9666 * pre-existing markup, and creates required markup if it is not already present.
9667 * @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.
9668 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9669 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
9670 * <code>&#60;li&#62;</code> element of the menu bar item.
9671 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9672 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
9673 * specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
9674 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
9675 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
9676 * the <code>&#60;option&#62;</code> element of the menu bar item.
9677 * @param {Object} p_oConfig Optional. Object literal specifying the 
9678 * configuration for the menu bar item. See configuration class documentation 
9679 * for more details.
9680 */
9681 init: function(p_oObject, p_oConfig) {
9682
9683     if(!this.SUBMENU_TYPE) {
9684
9685         this.SUBMENU_TYPE = YAHOO.widget.Menu;
9686
9687     }
9688
9689
9690     /* 
9691         Call the init of the superclass (YAHOO.widget.MenuItem)
9692         Note: We don't pass the user config in here yet 
9693         because we only want it executed once, at the lowest 
9694         subclass level.
9695     */ 
9696
9697     YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject);  
9698
9699
9700     var oConfig = this.cfg;
9701
9702     if(p_oConfig) {
9703
9704         oConfig.applyConfig(p_oConfig, true);
9705
9706     }
9707
9708     oConfig.fireQueue();
9709
9710 },
9711
9712
9713
9714 // Constants
9715
9716
9717 /**
9718 * @property CSS_CLASS_NAME
9719 * @description String representing the CSS class(es) to be applied to the 
9720 * <code>&#60;li&#62;</code> element of the menu bar item.
9721 * @default "yuimenubaritem"
9722 * @final
9723 * @type String
9724 */
9725 CSS_CLASS_NAME: "yuimenubaritem",
9726
9727
9728 /**
9729 * @property CSS_LABEL_CLASS_NAME
9730 * @description String representing the CSS class(es) to be applied to the 
9731 * menu bar item's <code>&#60;a&#62;</code> element.
9732 * @default "yuimenubaritemlabel"
9733 * @final
9734 * @type String
9735 */
9736 CSS_LABEL_CLASS_NAME: "yuimenubaritemlabel",
9737
9738
9739
9740 // Public methods
9741
9742
9743 /**
9744 * @method toString
9745 * @description Returns a string representing the menu bar item.
9746 * @return {String}
9747 */
9748 toString: function() {
9749
9750     var sReturnVal = "MenuBarItem";
9751
9752     if(this.cfg && this.cfg.getProperty("text")) {
9753
9754         sReturnVal += (": " + this.cfg.getProperty("text"));
9755
9756     }
9757
9758     return sReturnVal;
9759
9760 }
9761     
9762 }); // END YAHOO.lang.extend
9763 YAHOO.register("menu", YAHOO.widget.Menu, {version: "2.9.0", build: "2800"});