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