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