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