PageRenderTime 60ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/hippo/src/main/webapp/yui/menu/menu.js

http://hdbc.googlecode.com/
JavaScript | 2672 lines | 1057 code | 903 blank | 712 comment | 212 complexity | a7e26f3d475ea2390af24915b94adc59 MD5 | raw file
  1. /*
  2. Copyright (c) 2009, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.7.0
  6. */
  7. /**
  8. * @module menu
  9. * @description <p>The Menu family of components features a collection of
  10. * controls that make it easy to add menus to your website or web application.
  11. * With the Menu Controls you can create website fly-out menus, customized
  12. * context menus, or application-style menu bars with just a small amount of
  13. * scripting.</p><p>The Menu family of controls features:</p>
  14. * <ul>
  15. * <li>Keyboard and mouse navigation.</li>
  16. * <li>A rich event model that provides access to all of a menu's
  17. * interesting moments.</li>
  18. * <li>Support for
  19. * <a href="http://en.wikipedia.org/wiki/Progressive_Enhancement">Progressive
  20. * Enhancement</a>; Menus can be created from simple,
  21. * semantic markup on the page or purely through JavaScript.</li>
  22. * </ul>
  23. * @title Menu
  24. * @namespace YAHOO.widget
  25. * @requires Event, Dom, Container
  26. */
  27. (function () {
  28. var _DIV = "DIV",
  29. _HD = "hd",
  30. _BD = "bd",
  31. _FT = "ft",
  32. _LI = "LI",
  33. _DISABLED = "disabled",
  34. _MOUSEOVER = "mouseover",
  35. _MOUSEOUT = "mouseout",
  36. _MOUSEDOWN = "mousedown",
  37. _MOUSEUP = "mouseup",
  38. _FOCUS = YAHOO.env.ua.ie ? "focusin" : "focus",
  39. _CLICK = "click",
  40. _KEYDOWN = "keydown",
  41. _KEYUP = "keyup",
  42. _KEYPRESS = "keypress",
  43. _CLICK_TO_HIDE = "clicktohide",
  44. _POSITION = "position",
  45. _DYNAMIC = "dynamic",
  46. _SHOW_DELAY = "showdelay",
  47. _SELECTED = "selected",
  48. _VISIBLE = "visible",
  49. _UL = "UL",
  50. _MENUMANAGER = "MenuManager",
  51. Dom = YAHOO.util.Dom,
  52. Event = YAHOO.util.Event,
  53. Lang = YAHOO.lang;
  54. /**
  55. * Singleton that manages a collection of all menus and menu items. Listens
  56. * for DOM events at the document level and dispatches the events to the
  57. * corresponding menu or menu item.
  58. *
  59. * @namespace YAHOO.widget
  60. * @class MenuManager
  61. * @static
  62. */
  63. YAHOO.widget.MenuManager = function () {
  64. // Private member variables
  65. // Flag indicating if the DOM event handlers have been attached
  66. var m_bInitializedEventHandlers = false,
  67. // Collection of menus
  68. m_oMenus = {},
  69. // Collection of visible menus
  70. m_oVisibleMenus = {},
  71. // Collection of menu items
  72. m_oItems = {},
  73. // Map of DOM event types to their equivalent CustomEvent types
  74. m_oEventTypes = {
  75. "click": "clickEvent",
  76. "mousedown": "mouseDownEvent",
  77. "mouseup": "mouseUpEvent",
  78. "mouseover": "mouseOverEvent",
  79. "mouseout": "mouseOutEvent",
  80. "keydown": "keyDownEvent",
  81. "keyup": "keyUpEvent",
  82. "keypress": "keyPressEvent",
  83. "focus": "focusEvent",
  84. "focusin": "focusEvent",
  85. "blur": "blurEvent",
  86. "focusout": "blurEvent"
  87. },
  88. // The element in the DOM that currently has focus
  89. m_oFocusedElement = null,
  90. m_oFocusedMenuItem = null;
  91. // Private methods
  92. /**
  93. * @method getMenuRootElement
  94. * @description Finds the root DIV node of a menu or the root LI node of
  95. * a menu item.
  96. * @private
  97. * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  98. * level-one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object
  99. * specifying an HTML element.
  100. */
  101. function getMenuRootElement(p_oElement) {
  102. var oParentNode,
  103. returnVal;
  104. if (p_oElement && p_oElement.tagName) {
  105. switch (p_oElement.tagName.toUpperCase()) {
  106. case _DIV:
  107. oParentNode = p_oElement.parentNode;
  108. // Check if the DIV is the inner "body" node of a menu
  109. if ((
  110. Dom.hasClass(p_oElement, _HD) ||
  111. Dom.hasClass(p_oElement, _BD) ||
  112. Dom.hasClass(p_oElement, _FT)
  113. ) &&
  114. oParentNode &&
  115. oParentNode.tagName &&
  116. oParentNode.tagName.toUpperCase() == _DIV) {
  117. returnVal = oParentNode;
  118. }
  119. else {
  120. returnVal = p_oElement;
  121. }
  122. break;
  123. case _LI:
  124. returnVal = p_oElement;
  125. break;
  126. default:
  127. oParentNode = p_oElement.parentNode;
  128. if (oParentNode) {
  129. returnVal = getMenuRootElement(oParentNode);
  130. }
  131. break;
  132. }
  133. }
  134. return returnVal;
  135. }
  136. // Private event handlers
  137. /**
  138. * @method onDOMEvent
  139. * @description Generic, global event handler for all of a menu's
  140. * DOM-based events. This listens for events against the document
  141. * object. If the target of a given event is a member of a menu or
  142. * menu item's DOM, the instance's corresponding Custom Event is fired.
  143. * @private
  144. * @param {Event} p_oEvent Object representing the DOM event object
  145. * passed back by the event utility (YAHOO.util.Event).
  146. */
  147. function onDOMEvent(p_oEvent) {
  148. // Get the target node of the DOM event
  149. var oTarget = Event.getTarget(p_oEvent),
  150. // See if the target of the event was a menu, or a menu item
  151. oElement = getMenuRootElement(oTarget),
  152. sCustomEventType,
  153. sTagName,
  154. sId,
  155. oMenuItem,
  156. oMenu;
  157. if (oElement) {
  158. sTagName = oElement.tagName.toUpperCase();
  159. if (sTagName == _LI) {
  160. sId = oElement.id;
  161. if (sId && m_oItems[sId]) {
  162. oMenuItem = m_oItems[sId];
  163. oMenu = oMenuItem.parent;
  164. }
  165. }
  166. else if (sTagName == _DIV) {
  167. if (oElement.id) {
  168. oMenu = m_oMenus[oElement.id];
  169. }
  170. }
  171. }
  172. if (oMenu) {
  173. sCustomEventType = m_oEventTypes[p_oEvent.type];
  174. // Fire the Custom Event that corresponds the current DOM event
  175. if (oMenuItem && !oMenuItem.cfg.getProperty(_DISABLED)) {
  176. oMenuItem[sCustomEventType].fire(p_oEvent);
  177. }
  178. oMenu[sCustomEventType].fire(p_oEvent, oMenuItem);
  179. }
  180. else if (p_oEvent.type == _MOUSEDOWN) {
  181. /*
  182. If the target of the event wasn't a menu, hide all
  183. dynamically positioned menus
  184. */
  185. for (var i in m_oVisibleMenus) {
  186. if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {
  187. oMenu = m_oVisibleMenus[i];
  188. if (oMenu.cfg.getProperty(_CLICK_TO_HIDE) &&
  189. !(oMenu instanceof YAHOO.widget.MenuBar) &&
  190. oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {
  191. oMenu.hide();
  192. }
  193. else {
  194. if (oMenu.cfg.getProperty(_SHOW_DELAY) > 0) {
  195. oMenu._cancelShowDelay();
  196. }
  197. if (oMenu.activeItem) {
  198. oMenu.activeItem.blur();
  199. oMenu.activeItem.cfg.setProperty(_SELECTED, false);
  200. oMenu.activeItem = null;
  201. }
  202. }
  203. }
  204. }
  205. }
  206. else if (p_oEvent.type == _FOCUS) {
  207. m_oFocusedElement = oTarget;
  208. }
  209. }
  210. /**
  211. * @method onMenuDestroy
  212. * @description "destroy" event handler for a menu.
  213. * @private
  214. * @param {String} p_sType String representing the name of the event
  215. * that was fired.
  216. * @param {Array} p_aArgs Array of arguments sent when the event
  217. * was fired.
  218. * @param {YAHOO.widget.Menu} p_oMenu The menu that fired the event.
  219. */
  220. function onMenuDestroy(p_sType, p_aArgs, p_oMenu) {
  221. if (m_oMenus[p_oMenu.id]) {
  222. this.removeMenu(p_oMenu);
  223. }
  224. }
  225. /**
  226. * @method onMenuFocus
  227. * @description "focus" event handler for a MenuItem instance.
  228. * @private
  229. * @param {String} p_sType String representing the name of the event
  230. * that was fired.
  231. * @param {Array} p_aArgs Array of arguments sent when the event
  232. * was fired.
  233. */
  234. function onMenuFocus(p_sType, p_aArgs) {
  235. var oItem = p_aArgs[1];
  236. if (oItem) {
  237. m_oFocusedMenuItem = oItem;
  238. }
  239. }
  240. /**
  241. * @method onMenuBlur
  242. * @description "blur" event handler for a MenuItem instance.
  243. * @private
  244. * @param {String} p_sType String representing the name of the event
  245. * that was fired.
  246. * @param {Array} p_aArgs Array of arguments sent when the event
  247. * was fired.
  248. */
  249. function onMenuBlur(p_sType, p_aArgs) {
  250. m_oFocusedMenuItem = null;
  251. }
  252. /**
  253. * @method onMenuHide
  254. * @description "hide" event handler for a Menu instance.
  255. * @private
  256. * @param {String} p_sType String representing the name of the event
  257. * that was fired.
  258. * @param {Array} p_aArgs Array of arguments sent when the event
  259. * was fired.
  260. * @param <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  261. * level-one-html.html#ID-58190037">p_oFocusedElement</a> The HTML element that had focus
  262. * prior to the Menu being made visible
  263. */
  264. function onMenuHide(p_sType, p_aArgs, p_oFocusedElement) {
  265. /*
  266. Restore focus to the element in the DOM that had focus prior to the Menu
  267. being made visible
  268. */
  269. if (p_oFocusedElement && p_oFocusedElement.focus) {
  270. try {
  271. p_oFocusedElement.focus();
  272. }
  273. catch(ex) {
  274. }
  275. }
  276. this.hideEvent.unsubscribe(onMenuHide, p_oFocusedElement);
  277. }
  278. /**
  279. * @method onMenuShow
  280. * @description "show" event handler for a MenuItem instance.
  281. * @private
  282. * @param {String} p_sType String representing the name of the event
  283. * that was fired.
  284. * @param {Array} p_aArgs Array of arguments sent when the event
  285. * was fired.
  286. */
  287. function onMenuShow(p_sType, p_aArgs) {
  288. /*
  289. Dynamically positioned, root Menus focus themselves when visible, and will then,
  290. when hidden, restore focus to the UI control that had focus before the Menu was
  291. made visible
  292. */
  293. if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) {
  294. this.hideEvent.subscribe(onMenuHide, m_oFocusedElement);
  295. this.focus();
  296. }
  297. }
  298. /**
  299. * @method onMenuVisibleConfigChange
  300. * @description Event handler for when the "visible" configuration
  301. * property of a Menu instance changes.
  302. * @private
  303. * @param {String} p_sType String representing the name of the event
  304. * that was fired.
  305. * @param {Array} p_aArgs Array of arguments sent when the event
  306. * was fired.
  307. */
  308. function onMenuVisibleConfigChange(p_sType, p_aArgs) {
  309. var bVisible = p_aArgs[0],
  310. sId = this.id;
  311. if (bVisible) {
  312. m_oVisibleMenus[sId] = this;
  313. }
  314. else if (m_oVisibleMenus[sId]) {
  315. delete m_oVisibleMenus[sId];
  316. }
  317. }
  318. /**
  319. * @method onItemDestroy
  320. * @description "destroy" event handler for a MenuItem instance.
  321. * @private
  322. * @param {String} p_sType String representing the name of the event
  323. * that was fired.
  324. * @param {Array} p_aArgs Array of arguments sent when the event
  325. * was fired.
  326. */
  327. function onItemDestroy(p_sType, p_aArgs) {
  328. removeItem(this);
  329. }
  330. /**
  331. * @method removeItem
  332. * @description Removes a MenuItem instance from the MenuManager's collection of MenuItems.
  333. * @private
  334. * @param {MenuItem} p_oMenuItem The MenuItem instance to be removed.
  335. */
  336. function removeItem(p_oMenuItem) {
  337. var sId = p_oMenuItem.id;
  338. if (sId && m_oItems[sId]) {
  339. if (m_oFocusedMenuItem == p_oMenuItem) {
  340. m_oFocusedMenuItem = null;
  341. }
  342. delete m_oItems[sId];
  343. p_oMenuItem.destroyEvent.unsubscribe(onItemDestroy);
  344. }
  345. }
  346. /**
  347. * @method onItemAdded
  348. * @description "itemadded" event handler for a Menu instance.
  349. * @private
  350. * @param {String} p_sType String representing the name of the event
  351. * that was fired.
  352. * @param {Array} p_aArgs Array of arguments sent when the event
  353. * was fired.
  354. */
  355. function onItemAdded(p_sType, p_aArgs) {
  356. var oItem = p_aArgs[0],
  357. sId;
  358. if (oItem instanceof YAHOO.widget.MenuItem) {
  359. sId = oItem.id;
  360. if (!m_oItems[sId]) {
  361. m_oItems[sId] = oItem;
  362. oItem.destroyEvent.subscribe(onItemDestroy);
  363. }
  364. }
  365. }
  366. return {
  367. // Privileged methods
  368. /**
  369. * @method addMenu
  370. * @description Adds a menu to the collection of known menus.
  371. * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu
  372. * instance to be added.
  373. */
  374. addMenu: function (p_oMenu) {
  375. var oDoc;
  376. if (p_oMenu instanceof YAHOO.widget.Menu && p_oMenu.id &&
  377. !m_oMenus[p_oMenu.id]) {
  378. m_oMenus[p_oMenu.id] = p_oMenu;
  379. if (!m_bInitializedEventHandlers) {
  380. oDoc = document;
  381. Event.on(oDoc, _MOUSEOVER, onDOMEvent, this, true);
  382. Event.on(oDoc, _MOUSEOUT, onDOMEvent, this, true);
  383. Event.on(oDoc, _MOUSEDOWN, onDOMEvent, this, true);
  384. Event.on(oDoc, _MOUSEUP, onDOMEvent, this, true);
  385. Event.on(oDoc, _CLICK, onDOMEvent, this, true);
  386. Event.on(oDoc, _KEYDOWN, onDOMEvent, this, true);
  387. Event.on(oDoc, _KEYUP, onDOMEvent, this, true);
  388. Event.on(oDoc, _KEYPRESS, onDOMEvent, this, true);
  389. Event.onFocus(oDoc, onDOMEvent, this, true);
  390. Event.onBlur(oDoc, onDOMEvent, this, true);
  391. m_bInitializedEventHandlers = true;
  392. }
  393. p_oMenu.cfg.subscribeToConfigEvent(_VISIBLE, onMenuVisibleConfigChange);
  394. p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this);
  395. p_oMenu.itemAddedEvent.subscribe(onItemAdded);
  396. p_oMenu.focusEvent.subscribe(onMenuFocus);
  397. p_oMenu.blurEvent.subscribe(onMenuBlur);
  398. p_oMenu.showEvent.subscribe(onMenuShow);
  399. }
  400. },
  401. /**
  402. * @method removeMenu
  403. * @description Removes a menu from the collection of known menus.
  404. * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu
  405. * instance to be removed.
  406. */
  407. removeMenu: function (p_oMenu) {
  408. var sId,
  409. aItems,
  410. i;
  411. if (p_oMenu) {
  412. sId = p_oMenu.id;
  413. if ((sId in m_oMenus) && (m_oMenus[sId] == p_oMenu)) {
  414. // Unregister each menu item
  415. aItems = p_oMenu.getItems();
  416. if (aItems && aItems.length > 0) {
  417. i = aItems.length - 1;
  418. do {
  419. removeItem(aItems[i]);
  420. }
  421. while (i--);
  422. }
  423. // Unregister the menu
  424. delete m_oMenus[sId];
  425. /*
  426. Unregister the menu from the collection of
  427. visible menus
  428. */
  429. if ((sId in m_oVisibleMenus) && (m_oVisibleMenus[sId] == p_oMenu)) {
  430. delete m_oVisibleMenus[sId];
  431. }
  432. // Unsubscribe event listeners
  433. if (p_oMenu.cfg) {
  434. p_oMenu.cfg.unsubscribeFromConfigEvent(_VISIBLE,
  435. onMenuVisibleConfigChange);
  436. }
  437. p_oMenu.destroyEvent.unsubscribe(onMenuDestroy,
  438. p_oMenu);
  439. p_oMenu.itemAddedEvent.unsubscribe(onItemAdded);
  440. p_oMenu.focusEvent.unsubscribe(onMenuFocus);
  441. p_oMenu.blurEvent.unsubscribe(onMenuBlur);
  442. }
  443. }
  444. },
  445. /**
  446. * @method hideVisible
  447. * @description Hides all visible, dynamically positioned menus
  448. * (excluding instances of YAHOO.widget.MenuBar).
  449. */
  450. hideVisible: function () {
  451. var oMenu;
  452. for (var i in m_oVisibleMenus) {
  453. if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {
  454. oMenu = m_oVisibleMenus[i];
  455. if (!(oMenu instanceof YAHOO.widget.MenuBar) &&
  456. oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {
  457. oMenu.hide();
  458. }
  459. }
  460. }
  461. },
  462. /**
  463. * @method getVisible
  464. * @description Returns a collection of all visible menus registered
  465. * with the menu manger.
  466. * @return {Object}
  467. */
  468. getVisible: function () {
  469. return m_oVisibleMenus;
  470. },
  471. /**
  472. * @method getMenus
  473. * @description Returns a collection of all menus registered with the
  474. * menu manger.
  475. * @return {Object}
  476. */
  477. getMenus: function () {
  478. return m_oMenus;
  479. },
  480. /**
  481. * @method getMenu
  482. * @description Returns a menu with the specified id.
  483. * @param {String} p_sId String specifying the id of the
  484. * <code>&#60;div&#62;</code> element representing the menu to
  485. * be retrieved.
  486. * @return {YAHOO.widget.Menu}
  487. */
  488. getMenu: function (p_sId) {
  489. var returnVal;
  490. if (p_sId in m_oMenus) {
  491. returnVal = m_oMenus[p_sId];
  492. }
  493. return returnVal;
  494. },
  495. /**
  496. * @method getMenuItem
  497. * @description Returns a menu item with the specified id.
  498. * @param {String} p_sId String specifying the id of the
  499. * <code>&#60;li&#62;</code> element representing the menu item to
  500. * be retrieved.
  501. * @return {YAHOO.widget.MenuItem}
  502. */
  503. getMenuItem: function (p_sId) {
  504. var returnVal;
  505. if (p_sId in m_oItems) {
  506. returnVal = m_oItems[p_sId];
  507. }
  508. return returnVal;
  509. },
  510. /**
  511. * @method getMenuItemGroup
  512. * @description Returns an array of menu item instances whose
  513. * corresponding <code>&#60;li&#62;</code> elements are child
  514. * nodes of the <code>&#60;ul&#62;</code> element with the
  515. * specified id.
  516. * @param {String} p_sId String specifying the id of the
  517. * <code>&#60;ul&#62;</code> element representing the group of
  518. * menu items to be retrieved.
  519. * @return {Array}
  520. */
  521. getMenuItemGroup: function (p_sId) {
  522. var oUL = Dom.get(p_sId),
  523. aItems,
  524. oNode,
  525. oItem,
  526. sId,
  527. returnVal;
  528. if (oUL && oUL.tagName && oUL.tagName.toUpperCase() == _UL) {
  529. oNode = oUL.firstChild;
  530. if (oNode) {
  531. aItems = [];
  532. do {
  533. sId = oNode.id;
  534. if (sId) {
  535. oItem = this.getMenuItem(sId);
  536. if (oItem) {
  537. aItems[aItems.length] = oItem;
  538. }
  539. }
  540. }
  541. while ((oNode = oNode.nextSibling));
  542. if (aItems.length > 0) {
  543. returnVal = aItems;
  544. }
  545. }
  546. }
  547. return returnVal;
  548. },
  549. /**
  550. * @method getFocusedMenuItem
  551. * @description Returns a reference to the menu item that currently
  552. * has focus.
  553. * @return {YAHOO.widget.MenuItem}
  554. */
  555. getFocusedMenuItem: function () {
  556. return m_oFocusedMenuItem;
  557. },
  558. /**
  559. * @method getFocusedMenu
  560. * @description Returns a reference to the menu that currently
  561. * has focus.
  562. * @return {YAHOO.widget.Menu}
  563. */
  564. getFocusedMenu: function () {
  565. var returnVal;
  566. if (m_oFocusedMenuItem) {
  567. returnVal = m_oFocusedMenuItem.parent.getRoot();
  568. }
  569. return returnVal;
  570. },
  571. /**
  572. * @method toString
  573. * @description Returns a string representing the menu manager.
  574. * @return {String}
  575. */
  576. toString: function () {
  577. return _MENUMANAGER;
  578. }
  579. };
  580. }();
  581. })();
  582. (function () {
  583. var Lang = YAHOO.lang,
  584. // String constants
  585. _MENU = "Menu",
  586. _DIV_UPPERCASE = "DIV",
  587. _DIV_LOWERCASE = "div",
  588. _ID = "id",
  589. _SELECT = "SELECT",
  590. _XY = "xy",
  591. _Y = "y",
  592. _UL_UPPERCASE = "UL",
  593. _UL_LOWERCASE = "ul",
  594. _FIRST_OF_TYPE = "first-of-type",
  595. _LI = "LI",
  596. _OPTGROUP = "OPTGROUP",
  597. _OPTION = "OPTION",
  598. _DISABLED = "disabled",
  599. _NONE = "none",
  600. _SELECTED = "selected",
  601. _GROUP_INDEX = "groupindex",
  602. _INDEX = "index",
  603. _SUBMENU = "submenu",
  604. _VISIBLE = "visible",
  605. _HIDE_DELAY = "hidedelay",
  606. _POSITION = "position",
  607. _DYNAMIC = "dynamic",
  608. _STATIC = "static",
  609. _DYNAMIC_STATIC = _DYNAMIC + "," + _STATIC,
  610. _WINDOWS = "windows",
  611. _URL = "url",
  612. _HASH = "#",
  613. _TARGET = "target",
  614. _MAX_HEIGHT = "maxheight",
  615. _TOP_SCROLLBAR = "topscrollbar",
  616. _BOTTOM_SCROLLBAR = "bottomscrollbar",
  617. _UNDERSCORE = "_",
  618. _TOP_SCROLLBAR_DISABLED = _TOP_SCROLLBAR + _UNDERSCORE + _DISABLED,
  619. _BOTTOM_SCROLLBAR_DISABLED = _BOTTOM_SCROLLBAR + _UNDERSCORE + _DISABLED,
  620. _MOUSEMOVE = "mousemove",
  621. _SHOW_DELAY = "showdelay",
  622. _SUBMENU_HIDE_DELAY = "submenuhidedelay",
  623. _IFRAME = "iframe",
  624. _CONSTRAIN_TO_VIEWPORT = "constraintoviewport",
  625. _PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
  626. _SUBMENU_ALIGNMENT = "submenualignment",
  627. _AUTO_SUBMENU_DISPLAY = "autosubmenudisplay",
  628. _CLICK_TO_HIDE = "clicktohide",
  629. _CONTAINER = "container",
  630. _SCROLL_INCREMENT = "scrollincrement",
  631. _MIN_SCROLL_HEIGHT = "minscrollheight",
  632. _CLASSNAME = "classname",
  633. _SHADOW = "shadow",
  634. _KEEP_OPEN = "keepopen",
  635. _HD = "hd",
  636. _HAS_TITLE = "hastitle",
  637. _CONTEXT = "context",
  638. _EMPTY_STRING = "",
  639. _MOUSEDOWN = "mousedown",
  640. _KEYDOWN = "keydown",
  641. _HEIGHT = "height",
  642. _WIDTH = "width",
  643. _PX = "px",
  644. _EFFECT = "effect",
  645. _MONITOR_RESIZE = "monitorresize",
  646. _DISPLAY = "display",
  647. _BLOCK = "block",
  648. _VISIBILITY = "visibility",
  649. _ABSOLUTE = "absolute",
  650. _ZINDEX = "zindex",
  651. _YUI_MENU_BODY_SCROLLED = "yui-menu-body-scrolled",
  652. _NON_BREAKING_SPACE = "&#32;",
  653. _SPACE = " ",
  654. _MOUSEOVER = "mouseover",
  655. _MOUSEOUT = "mouseout",
  656. _ITEM_ADDED = "itemAdded",
  657. _ITEM_REMOVED = "itemRemoved",
  658. _HIDDEN = "hidden",
  659. _YUI_MENU_SHADOW = "yui-menu-shadow",
  660. _YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + "-visible",
  661. _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + _SPACE + _YUI_MENU_SHADOW_VISIBLE;
  662. /**
  663. * The Menu class creates a container that holds a vertical list representing
  664. * a set of options or commands. Menu is the base class for all
  665. * menu containers.
  666. * @param {String} p_oElement String specifying the id attribute of the
  667. * <code>&#60;div&#62;</code> element of the menu.
  668. * @param {String} p_oElement String specifying the id attribute of the
  669. * <code>&#60;select&#62;</code> element to be used as the data source
  670. * for the menu.
  671. * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  672. * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
  673. * specifying the <code>&#60;div&#62;</code> element of the menu.
  674. * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  675. * level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement
  676. * Object specifying the <code>&#60;select&#62;</code> element to be used as
  677. * the data source for the menu.
  678. * @param {Object} p_oConfig Optional. Object literal specifying the
  679. * configuration for the menu. See configuration class documentation for
  680. * more details.
  681. * @namespace YAHOO.widget
  682. * @class Menu
  683. * @constructor
  684. * @extends YAHOO.widget.Overlay
  685. */
  686. YAHOO.widget.Menu = function (p_oElement, p_oConfig) {
  687. if (p_oConfig) {
  688. this.parent = p_oConfig.parent;
  689. this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload;
  690. this.itemData = p_oConfig.itemData || p_oConfig.itemdata;
  691. }
  692. YAHOO.widget.Menu.superclass.constructor.call(this, p_oElement, p_oConfig);
  693. };
  694. /**
  695. * @method checkPosition
  696. * @description Checks to make sure that the value of the "position" property
  697. * is one of the supported strings. Returns true if the position is supported.
  698. * @private
  699. * @param {Object} p_sPosition String specifying the position of the menu.
  700. * @return {Boolean}
  701. */
  702. function checkPosition(p_sPosition) {
  703. var returnVal = false;
  704. if (Lang.isString(p_sPosition)) {
  705. returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1);
  706. }
  707. return returnVal;
  708. }
  709. var Dom = YAHOO.util.Dom,
  710. Event = YAHOO.util.Event,
  711. Module = YAHOO.widget.Module,
  712. Overlay = YAHOO.widget.Overlay,
  713. Menu = YAHOO.widget.Menu,
  714. MenuManager = YAHOO.widget.MenuManager,
  715. CustomEvent = YAHOO.util.CustomEvent,
  716. UA = YAHOO.env.ua,
  717. m_oShadowTemplate,
  718. EVENT_TYPES = [
  719. ["mouseOverEvent", _MOUSEOVER],
  720. ["mouseOutEvent", _MOUSEOUT],
  721. ["mouseDownEvent", _MOUSEDOWN],
  722. ["mouseUpEvent", "mouseup"],
  723. ["clickEvent", "click"],
  724. ["keyPressEvent", "keypress"],
  725. ["keyDownEvent", _KEYDOWN],
  726. ["keyUpEvent", "keyup"],
  727. ["focusEvent", "focus"],
  728. ["blurEvent", "blur"],
  729. ["itemAddedEvent", _ITEM_ADDED],
  730. ["itemRemovedEvent", _ITEM_REMOVED]
  731. ],
  732. VISIBLE_CONFIG = {
  733. key: _VISIBLE,
  734. value: false,
  735. validator: Lang.isBoolean
  736. },
  737. CONSTRAIN_TO_VIEWPORT_CONFIG = {
  738. key: _CONSTRAIN_TO_VIEWPORT,
  739. value: true,
  740. validator: Lang.isBoolean,
  741. supercedes: [_IFRAME,"x",_Y,_XY]
  742. },
  743. PREVENT_CONTEXT_OVERLAP_CONFIG = {
  744. key: _PREVENT_CONTEXT_OVERLAP,
  745. value: true,
  746. validator: Lang.isBoolean,
  747. supercedes: [_CONSTRAIN_TO_VIEWPORT]
  748. },
  749. POSITION_CONFIG = {
  750. key: _POSITION,
  751. value: _DYNAMIC,
  752. validator: checkPosition,
  753. supercedes: [_VISIBLE, _IFRAME]
  754. },
  755. SUBMENU_ALIGNMENT_CONFIG = {
  756. key: _SUBMENU_ALIGNMENT,
  757. value: ["tl","tr"]
  758. },
  759. AUTO_SUBMENU_DISPLAY_CONFIG = {
  760. key: _AUTO_SUBMENU_DISPLAY,
  761. value: true,
  762. validator: Lang.isBoolean,
  763. suppressEvent: true
  764. },
  765. SHOW_DELAY_CONFIG = {
  766. key: _SHOW_DELAY,
  767. value: 250,
  768. validator: Lang.isNumber,
  769. suppressEvent: true
  770. },
  771. HIDE_DELAY_CONFIG = {
  772. key: _HIDE_DELAY,
  773. value: 0,
  774. validator: Lang.isNumber,
  775. suppressEvent: true
  776. },
  777. SUBMENU_HIDE_DELAY_CONFIG = {
  778. key: _SUBMENU_HIDE_DELAY,
  779. value: 250,
  780. validator: Lang.isNumber,
  781. suppressEvent: true
  782. },
  783. CLICK_TO_HIDE_CONFIG = {
  784. key: _CLICK_TO_HIDE,
  785. value: true,
  786. validator: Lang.isBoolean,
  787. suppressEvent: true
  788. },
  789. CONTAINER_CONFIG = {
  790. key: _CONTAINER,
  791. suppressEvent: true
  792. },
  793. SCROLL_INCREMENT_CONFIG = {
  794. key: _SCROLL_INCREMENT,
  795. value: 1,
  796. validator: Lang.isNumber,
  797. supercedes: [_MAX_HEIGHT],
  798. suppressEvent: true
  799. },
  800. MIN_SCROLL_HEIGHT_CONFIG = {
  801. key: _MIN_SCROLL_HEIGHT,
  802. value: 90,
  803. validator: Lang.isNumber,
  804. supercedes: [_MAX_HEIGHT],
  805. suppressEvent: true
  806. },
  807. MAX_HEIGHT_CONFIG = {
  808. key: _MAX_HEIGHT,
  809. value: 0,
  810. validator: Lang.isNumber,
  811. supercedes: [_IFRAME],
  812. suppressEvent: true
  813. },
  814. CLASS_NAME_CONFIG = {
  815. key: _CLASSNAME,
  816. value: null,
  817. validator: Lang.isString,
  818. suppressEvent: true
  819. },
  820. DISABLED_CONFIG = {
  821. key: _DISABLED,
  822. value: false,
  823. validator: Lang.isBoolean,
  824. suppressEvent: true
  825. },
  826. SHADOW_CONFIG = {
  827. key: _SHADOW,
  828. value: true,
  829. validator: Lang.isBoolean,
  830. suppressEvent: true,
  831. supercedes: [_VISIBLE]
  832. },
  833. KEEP_OPEN_CONFIG = {
  834. key: _KEEP_OPEN,
  835. value: false,
  836. validator: Lang.isBoolean
  837. };
  838. YAHOO.lang.extend(Menu, Overlay, {
  839. // Constants
  840. /**
  841. * @property CSS_CLASS_NAME
  842. * @description String representing the CSS class(es) to be applied to the
  843. * menu's <code>&#60;div&#62;</code> element.
  844. * @default "yuimenu"
  845. * @final
  846. * @type String
  847. */
  848. CSS_CLASS_NAME: "yuimenu",
  849. /**
  850. * @property ITEM_TYPE
  851. * @description Object representing the type of menu item to instantiate and
  852. * add when parsing the child nodes (either <code>&#60;li&#62;</code> element,
  853. * <code>&#60;optgroup&#62;</code> element or <code>&#60;option&#62;</code>)
  854. * of the menu's source HTML element.
  855. * @default YAHOO.widget.MenuItem
  856. * @final
  857. * @type YAHOO.widget.MenuItem
  858. */
  859. ITEM_TYPE: null,
  860. /**
  861. * @property GROUP_TITLE_TAG_NAME
  862. * @description String representing the tagname of the HTML element used to
  863. * title the menu's item groups.
  864. * @default H6
  865. * @final
  866. * @type String
  867. */
  868. GROUP_TITLE_TAG_NAME: "h6",
  869. /**
  870. * @property OFF_SCREEN_POSITION
  871. * @description Array representing the default x and y position that a menu
  872. * should have when it is positioned outside the viewport by the
  873. * "poistionOffScreen" method.
  874. * @default "-999em"
  875. * @final
  876. * @type String
  877. */
  878. OFF_SCREEN_POSITION: "-999em",
  879. // Private properties
  880. /**
  881. * @property _useHideDelay
  882. * @description Boolean indicating if the "mouseover" and "mouseout" event
  883. * handlers used for hiding the menu via a call to "YAHOO.lang.later" have
  884. * already been assigned.
  885. * @default false
  886. * @private
  887. * @type Boolean
  888. */
  889. _useHideDelay: false,
  890. /**
  891. * @property _bHandledMouseOverEvent
  892. * @description Boolean indicating the current state of the menu's
  893. * "mouseover" event.
  894. * @default false
  895. * @private
  896. * @type Boolean
  897. */
  898. _bHandledMouseOverEvent: false,
  899. /**
  900. * @property _bHandledMouseOutEvent
  901. * @description Boolean indicating the current state of the menu's
  902. * "mouseout" event.
  903. * @default false
  904. * @private
  905. * @type Boolean
  906. */
  907. _bHandledMouseOutEvent: false,
  908. /**
  909. * @property _aGroupTitleElements
  910. * @description Array of HTML element used to title groups of menu items.
  911. * @default []
  912. * @private
  913. * @type Array
  914. */
  915. _aGroupTitleElements: null,
  916. /**
  917. * @property _aItemGroups
  918. * @description Multi-dimensional Array representing the menu items as they
  919. * are grouped in the menu.
  920. * @default []
  921. * @private
  922. * @type Array
  923. */
  924. _aItemGroups: null,
  925. /**
  926. * @property _aListElements
  927. * @description Array of <code>&#60;ul&#62;</code> elements, each of which is
  928. * the parent node for each item's <code>&#60;li&#62;</code> element.
  929. * @default []
  930. * @private
  931. * @type Array
  932. */
  933. _aListElements: null,
  934. /**
  935. * @property _nCurrentMouseX
  936. * @description The current x coordinate of the mouse inside the area of
  937. * the menu.
  938. * @default 0
  939. * @private
  940. * @type Number
  941. */
  942. _nCurrentMouseX: 0,
  943. /**
  944. * @property _bStopMouseEventHandlers
  945. * @description Stops "mouseover," "mouseout," and "mousemove" event handlers
  946. * from executing.
  947. * @default false
  948. * @private
  949. * @type Boolean
  950. */
  951. _bStopMouseEventHandlers: false,
  952. /**
  953. * @property _sClassName
  954. * @description The current value of the "classname" configuration attribute.
  955. * @default null
  956. * @private
  957. * @type String
  958. */
  959. _sClassName: null,
  960. // Public properties
  961. /**
  962. * @property lazyLoad
  963. * @description Boolean indicating if the menu's "lazy load" feature is
  964. * enabled. If set to "true," initialization and rendering of the menu's
  965. * items will be deferred until the first time it is made visible. This
  966. * property should be set via the constructor using the configuration
  967. * object literal.
  968. * @default false
  969. * @type Boolean
  970. */
  971. lazyLoad: false,
  972. /**
  973. * @property itemData
  974. * @description Array of items to be added to the menu. The array can contain
  975. * strings representing the text for each item to be created, object literals
  976. * representing the menu item configuration properties, or MenuItem instances.
  977. * This property should be set via the constructor using the configuration
  978. * object literal.
  979. * @default null
  980. * @type Array
  981. */
  982. itemData: null,
  983. /**
  984. * @property activeItem
  985. * @description Object reference to the item in the menu that has is selected.
  986. * @default null
  987. * @type YAHOO.widget.MenuItem
  988. */
  989. activeItem: null,
  990. /**
  991. * @property parent
  992. * @description Object reference to the menu's parent menu or menu item.
  993. * This property can be set via the constructor using the configuration
  994. * object literal.
  995. * @default null
  996. * @type YAHOO.widget.MenuItem
  997. */
  998. parent: null,
  999. /**
  1000. * @property srcElement
  1001. * @description Object reference to the HTML element (either
  1002. * <code>&#60;select&#62;</code> or <code>&#60;div&#62;</code>) used to
  1003. * create the menu.
  1004. * @default null
  1005. * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  1006. * level-one-html.html#ID-94282980">HTMLSelectElement</a>|<a
  1007. * href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.
  1008. * html#ID-22445964">HTMLDivElement</a>
  1009. */
  1010. srcElement: null,
  1011. // Events
  1012. /**
  1013. * @event mouseOverEvent
  1014. * @description Fires when the mouse has entered the menu. Passes back
  1015. * the DOM Event object as an argument.
  1016. */
  1017. /**
  1018. * @event mouseOutEvent
  1019. * @description Fires when the mouse has left the menu. Passes back the DOM
  1020. * Event object as an argument.
  1021. * @type YAHOO.util.CustomEvent
  1022. */
  1023. /**
  1024. * @event mouseDownEvent
  1025. * @description Fires when the user mouses down on the menu. Passes back the
  1026. * DOM Event object as an argument.
  1027. * @type YAHOO.util.CustomEvent
  1028. */
  1029. /**
  1030. * @event mouseUpEvent
  1031. * @description Fires when the user releases a mouse button while the mouse is
  1032. * over the menu. Passes back the DOM Event object as an argument.
  1033. * @type YAHOO.util.CustomEvent
  1034. */
  1035. /**
  1036. * @event clickEvent
  1037. * @description Fires when the user clicks the on the menu. Passes back the
  1038. * DOM Event object as an argument.
  1039. * @type YAHOO.util.CustomEvent
  1040. */
  1041. /**
  1042. * @event keyPressEvent
  1043. * @description Fires when the user presses an alphanumeric key when one of the
  1044. * menu's items has focus. Passes back the DOM Event object as an argument.
  1045. * @type YAHOO.util.CustomEvent
  1046. */
  1047. /**
  1048. * @event keyDownEvent
  1049. * @description Fires when the user presses a key when one of the menu's items
  1050. * has focus. Passes back the DOM Event object as an argument.
  1051. * @type YAHOO.util.CustomEvent
  1052. */
  1053. /**
  1054. * @event keyUpEvent
  1055. * @description Fires when the user releases a key when one of the menu's items
  1056. * has focus. Passes back the DOM Event object as an argument.
  1057. * @type YAHOO.util.CustomEvent
  1058. */
  1059. /**
  1060. * @event itemAddedEvent
  1061. * @description Fires when an item is added to the menu.
  1062. * @type YAHOO.util.CustomEvent
  1063. */
  1064. /**
  1065. * @event itemRemovedEvent
  1066. * @description Fires when an item is removed to the menu.
  1067. * @type YAHOO.util.CustomEvent
  1068. */
  1069. /**
  1070. * @method init
  1071. * @description The Menu class's initialization method. This method is
  1072. * automatically called by the constructor, and sets up all DOM references
  1073. * for pre-existing markup, and creates required markup if it is not
  1074. * already present.
  1075. * @param {String} p_oElement String specifying the id attribute of the
  1076. * <code>&#60;div&#62;</code> element of the menu.
  1077. * @param {String} p_oElement String specifying the id attribute of the
  1078. * <code>&#60;select&#62;</code> element to be used as the data source
  1079. * for the menu.
  1080. * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  1081. * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
  1082. * specifying the <code>&#60;div&#62;</code> element of the menu.
  1083. * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
  1084. * level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement
  1085. * Object specifying the <code>&#60;select&#62;</code> element to be used as
  1086. * the data source for the menu.
  1087. * @param {Object} p_oConfig Optional. Object literal specifying the
  1088. * configuration for the menu. See configuration class documentation for
  1089. * more details.
  1090. */
  1091. init: function (p_oElement, p_oConfig) {
  1092. this._aItemGroups = [];
  1093. this._aListElements = [];
  1094. this._aGroupTitleElements = [];
  1095. if (!this.ITEM_TYPE) {
  1096. this.ITEM_TYPE = YAHOO.widget.MenuItem;
  1097. }
  1098. var oElement;
  1099. if (Lang.isString(p_oElement)) {
  1100. oElement = Dom.get(p_oElement);
  1101. }
  1102. else if (p_oElement.tagName) {
  1103. oElement = p_oElement;
  1104. }
  1105. if (oElement && oElement.tagName) {
  1106. switch(oElement.tagName.toUpperCase()) {
  1107. case _DIV_UPPERCASE:
  1108. this.srcElement = oElement;
  1109. if (!oElement.id) {
  1110. oElement.setAttribute(_ID, Dom.generateId());
  1111. }
  1112. /*
  1113. Note: we don't pass the user config in here yet
  1114. because we only want it executed once, at the lowest
  1115. subclass level.
  1116. */
  1117. Menu.superclass.init.call(this, oElement);
  1118. this.beforeInitEvent.fire(Menu);
  1119. break;
  1120. case _SELECT:
  1121. this.srcElement = oElement;
  1122. /*
  1123. The source element is not something that we can use
  1124. outright, so we need to create a new Overlay
  1125. Note: we don't pass the user config in here yet
  1126. because we only want it executed once, at the lowest
  1127. subclass level.
  1128. */
  1129. Menu.superclass.init.call(this, Dom.generateId());
  1130. this.beforeInitEvent.fire(Menu);
  1131. break;
  1132. }
  1133. }
  1134. else {
  1135. /*
  1136. Note: we don't pass the user config in here yet
  1137. because we only want it executed once, at the lowest
  1138. subclass level.
  1139. */
  1140. Menu.superclass.init.call(this, p_oElement);
  1141. this.beforeInitEvent.fire(Menu);
  1142. }
  1143. if (this.element) {
  1144. Dom.addClass(this.element, this.CSS_CLASS_NAME);
  1145. // Subscribe to Custom Events
  1146. this.initEvent.subscribe(this._onInit);
  1147. this.beforeRenderEvent.subscribe(this._onBeforeRender);
  1148. this.renderEvent.subscribe(this._onRender);
  1149. this.beforeShowEvent.subscribe(this._onBeforeShow);
  1150. this.hideEvent.subscribe(this._onHide);
  1151. this.showEvent.subscribe(this._onShow);
  1152. this.beforeHideEvent.subscribe(this._onBeforeHide);
  1153. this.mouseOverEvent.subscribe(this._onMouseOver);
  1154. this.mouseOutEvent.subscribe(this._onMouseOut);
  1155. this.clickEvent.subscribe(this._onClick);
  1156. this.keyDownEvent.subscribe(this._onKeyDown);
  1157. this.keyPressEvent.subscribe(this._onKeyPress);
  1158. this.blurEvent.subscribe(this._onBlur);
  1159. // Fixes an issue in Firefox 2 and Webkit where Dom's "getX" and "getY"
  1160. // methods return values that don't take scrollTop into consideration
  1161. if ((UA.gecko && UA.gecko < 1.9) || UA.webkit) {
  1162. this.cfg.subscribeToConfigEvent(_Y, this._onYChange);
  1163. }
  1164. if (p_oConfig) {
  1165. this.cfg.applyConfig(p_oConfig, true);
  1166. }
  1167. // Register the Menu instance with the MenuManager
  1168. MenuManager.addMenu(this);
  1169. this.initEvent.fire(Menu);
  1170. }
  1171. },
  1172. // Private methods
  1173. /**
  1174. * @method _initSubTree
  1175. * @description Iterates the childNodes of the source element to find nodes
  1176. * used to instantiate menu and menu items.
  1177. * @private
  1178. */
  1179. _initSubTree: function () {
  1180. var oSrcElement = this.srcElement,
  1181. sSrcElementTagName,
  1182. nGroup,
  1183. sGroupTitleTagName,
  1184. oNode,
  1185. aListElements,
  1186. nListElements,
  1187. i;
  1188. if (oSrcElement) {
  1189. sSrcElementTagName =
  1190. (oSrcElement.tagName && oSrcElement.tagName.toUpperCase());
  1191. if (sSrcElementTagName == _DIV_UPPERCASE) {
  1192. // Populate the collection of item groups and item group titles
  1193. oNode = this.body.firstChild;
  1194. if (oNode) {
  1195. nGroup = 0;
  1196. sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase();
  1197. do {
  1198. if (oNode && oNode.tagName) {
  1199. switch (oNode.tagName.toUpperCase()) {
  1200. case sGroupTitleTagName:
  1201. this._aGroupTitleElements[nGroup] = oNode;
  1202. break;
  1203. case _UL_UPPERCASE:
  1204. this._aListElements[nGroup] = oNode;
  1205. this._aItemGroups[nGroup] = [];
  1206. nGroup++;
  1207. break;
  1208. }
  1209. }
  1210. }
  1211. while ((oNode = oNode.nextSibling));
  1212. /*
  1213. Apply the "first-of-type" class to the first UL to mimic
  1214. the ":first-of-type" CSS3 psuedo class.
  1215. */
  1216. if (this._aListElements[0]) {
  1217. Dom.addClass(this._aListElements[0], _FIRST_OF_TYPE);
  1218. }
  1219. }
  1220. }
  1221. oNode = null;
  1222. if (sSrcElementTagName) {
  1223. switch (sSrcElementTagName) {
  1224. case _DIV_UPPERCASE:
  1225. aListElements = this._aListElements;
  1226. nListElements = aListElements.length;
  1227. if (nListElements > 0) {
  1228. i = nListElements - 1;
  1229. do {
  1230. oNode = aListElements[i].firstChild;
  1231. if (oNode) {
  1232. do {
  1233. if (oNode && oNode.tagName &&
  1234. oNode.tagName.toUpperCase() == _LI) {
  1235. this.addItem(new this.ITEM_TYPE(oNode,
  1236. { parent: this }), i);
  1237. }
  1238. }
  1239. while ((oNode = oNode.nextSibling));
  1240. }
  1241. }
  1242. while (i--);
  1243. }
  1244. break;
  1245. case _SELECT:
  1246. oNode = oSrcElement.firstChild;
  1247. do {
  1248. if (oNode && oNode.tagName) {
  1249. switch (oNode.tagName.toUpperCase()) {
  1250. case _OPTGROUP:
  1251. case _OPTION:
  1252. this.addItem(
  1253. new this.ITEM_TYPE(
  1254. oNode,
  1255. { parent: this }
  1256. )
  1257. );
  1258. break;
  1259. }
  1260. }
  1261. }
  1262. while ((oNode = oNode.nextSibling));
  1263. break;
  1264. }
  1265. }
  1266. }
  1267. },
  1268. /**
  1269. * @method _getFirstEnabledItem
  1270. * @description Returns the first enabled item in the menu.
  1271. * @return {YAHOO.widget.MenuItem}
  1272. * @private
  1273. */
  1274. _getFirstEnabledItem: function () {
  1275. var aItems = this.getItems(),
  1276. nItems = aItems.length,
  1277. oItem,
  1278. returnVal;
  1279. for(var i=0; i<nItems; i++) {
  1280. oItem = aItems[i];
  1281. if (oItem && !oItem.cfg.getProperty(_DISABLED) && oItem.element.style.display != _NONE) {
  1282. returnVal = oItem;
  1283. break;
  1284. }
  1285. }
  1286. return returnVal;
  1287. },
  1288. /**
  1289. * @method _addItemToGroup
  1290. * @description Adds a menu item to a group.
  1291. * @private
  1292. * @param {Number} p_nGroupIndex Number indicating the group to which the
  1293. * item belongs.
  1294. * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
  1295. * instance to be added to the menu.
  1296. * @param {String} p_oItem String specifying the text of the item to be added
  1297. * to the menu.
  1298. * @param {Object} p_oItem Object literal containing a set of menu item
  1299. * configuration properties.
  1300. * @param {Number} p_nItemIndex Optional. Number indicating the index at
  1301. * which the menu item should be added.
  1302. * @return {YAHOO.widget.MenuItem}
  1303. */
  1304. _addItemToGroup: function (p_nGroupIndex, p_oItem, p_nItemIndex) {
  1305. var oItem,
  1306. nGroupIndex,
  1307. aGroup,
  1308. oGroupItem,
  1309. bAppend,
  1310. oNextItemSibling,
  1311. nItemIndex,
  1312. returnVal;
  1313. function getNextItemSibling(p_aArray, p_nStartIndex) {
  1314. return (p_aArray[p_nStartIndex] || getNextItemSibling(p_aArray, (p_nStartIndex+1)));
  1315. }
  1316. if (p_oItem instanceof this.ITEM_TYPE) {
  1317. oItem = p_oItem;
  1318. oItem.parent = this;
  1319. }
  1320. else if (Lang.isString(p_oItem)) {
  1321. oItem = new this.ITEM_TYPE(p_oItem, { parent: this });
  1322. }
  1323. else if (Lang.isObject(p_oItem)) {
  1324. p_oItem.parent = this;
  1325. oItem = new this.ITEM_TYPE(p_oItem.text, p_oItem);
  1326. }
  1327. if (oItem) {
  1328. if (oItem.cfg.getProperty(_SELECTED)) {
  1329. this.activeItem = oItem;
  1330. }
  1331. nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0;
  1332. aGroup = this._getItemGroup(nGroupIndex);
  1333. if (!aGroup) {
  1334. aGroup = this._createItemGroup(nGroupIndex);
  1335. }
  1336. if (Lang.isNumber(p_nItemIndex)) {
  1337. bAppend = (p_nItemIndex >= aGroup.length);
  1338. if (aGroup[p_nItemIndex]) {
  1339. aGroup.splice(p_nItemIndex, 0, oItem);
  1340. }
  1341. else {
  1342. aGroup[p_nItemIndex] = oItem;
  1343. }
  1344. oGroupItem = aGroup[p_nItemIndex];
  1345. if (oGroupItem) {
  1346. if (bAppend && (!oGroupItem.element.parentNode ||
  1347. oGroupItem.element.parentNode.nodeType == 11)) {
  1348. this._aListElements[nGroupIndex].appendChild(oGroupItem.element);
  1349. }
  1350. else {
  1351. oNextItemSibling = getNextItemSibling(aGroup, (p_nItemIndex+1));
  1352. if (oNextItemSibling && (!oGroupItem.element.parentNode ||
  1353. oGroupItem.element.parentNode.nodeType == 11)) {
  1354. this._aListElements[nGroupIndex].insertBefore(
  1355. oGroupItem.element, oNextItemSibling.element);
  1356. }
  1357. }
  1358. oGroupItem.parent = this;
  1359. this._subscribeToItemEvents(oGroupItem);
  1360. this._configureSubmenu(oGroupItem);
  1361. this._updateItemProperties(nGroupIndex);
  1362. this.itemAddedEvent.fire(oGroupItem);
  1363. this.changeContentEvent.fire();
  1364. returnVal = oGroupItem;
  1365. }
  1366. }
  1367. else {
  1368. nItemIndex = aGroup.length;
  1369. aGroup[nItemIndex] = oItem;
  1370. oGroupItem = aGroup[nItemIndex];
  1371. if (oGroupItem) {
  1372. if (!Dom.isAncestor(this._aListElements[nGroupIndex], oGroupItem.element)) {
  1373. this._aListElements[nGroupIndex].appendChild(oGroupItem.element);
  1374. }
  1375. oGroupItem.element.setAttribute(_GROUP_INDEX, nGroupIndex);
  1376. oGroupItem.element.setAttribute(_INDEX, nItemIndex);
  1377. oGroupItem.parent = this;
  1378. oGroupItem.index = nItemIndex;
  1379. oGroupItem.groupIndex = nGroupIndex;
  1380. this._subscribeToItemEvents(oGroupItem);
  1381. this._configureSubmenu(oGroupItem);
  1382. if (nItemIndex === 0) {
  1383. Dom.addClass(oGroupItem.element, _FIRST_OF_TYPE);
  1384. }
  1385. this.itemAddedEvent.fire(oGroupItem);
  1386. this.changeContentEvent.fire();
  1387. returnVal = oGroupItem;
  1388. }
  1389. }
  1390. }
  1391. return returnVal;
  1392. },
  1393. /**
  1394. * @method _removeItemFromGroupByIndex
  1395. * @description Removes a menu item from a group by index. Returns the menu
  1396. * item that was removed.
  1397. * @private
  1398. * @param {Number} p_nGroupIndex Number indicating the group to which the menu
  1399. * item belongs.
  1400. * @param {Number} p_nItemIndex Number indicating the index of the menu item
  1401. * to be removed.
  1402. * @return {YAHOO.widget.MenuItem}
  1403. */
  1404. _removeItemFromGroupByIndex: function (p_nGroupIndex, p_nItemIndex) {
  1405. var nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0,
  1406. aGroup = this._getItemGroup(nGroupIndex),
  1407. aArray,
  1408. oItem,
  1409. oUL;
  1410. if (aGroup) {
  1411. aArray = aGroup.splice(p_nItemIndex, 1);
  1412. oItem = aArray[0];
  1413. if (oItem) {
  1414. // Update the index and className properties of each member
  1415. this._updateItemProperties(nGroupIndex);
  1416. if (aGroup.length === 0) {
  1417. // Remove the UL
  1418. oUL = this._aListElements[nGroupIndex];
  1419. if (this.body && oUL) {
  1420. this.body.removeChild(oUL);
  1421. }
  1422. // Remove the group from the array of items
  1423. this._aItemGroups.splice(nGroupIndex, 1);
  1424. // Remove the UL from the array of ULs
  1425. this._aListElements.splice(nGroupIndex, 1);
  1426. /*
  1427. Assign the "first-of-type" class to the new first UL
  1428. in the collection
  1429. */
  1430. oUL = this._aListElements[0];
  1431. if (oUL) {
  1432. Dom.addClass(oUL, _FIRST_OF_TYPE);
  1433. }
  1434. }
  1435. this.itemRemovedEvent.fire(oItem);
  1436. this.changeContentEvent.fire();
  1437. }
  1438. }
  1439. // Return a reference to the item that was removed
  1440. return oItem;
  1441. },
  1442. /**
  1443. * @method _removeItemFromGroupByValue
  1444. * @description Removes a menu item from a group by reference. Returns the
  1445. * menu item that was removed.
  1446. * @private
  1447. * @param {Number} p_nGroupIndex Number indicating the group to which the
  1448. * menu item belongs.
  1449. * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
  1450. * instance to be removed.
  1451. * @return {YAHOO.widget.MenuItem}
  1452. */
  1453. _removeItemFromGroupByValue: function (p_nGroupIndex, p_oItem) {
  1454. var aGroup = this._getItemGroup(p_nGroupIndex),
  1455. nItems,
  1456. nItemIndex,
  1457. returnVal,
  1458. i;
  1459. if (aGroup) {
  1460. nItems = aGroup.length;
  1461. nItemIndex = -1;
  1462. if (nItems > 0) {
  1463. i = nItems-1;
  1464. do {
  1465. if (aGroup[i] == p_oItem) {
  1466. nItemIndex = i;
  1467. break;
  1468. }
  1469. }
  1470. while (i--);
  1471. if (nItemIndex > -1) {
  1472. returnVal = this._removeItemFromGroupByIndex(p_nGroupIndex, nItemIndex);
  1473. }
  1474. }
  1475. }
  1476. return returnVal;
  1477. },
  1478. /**
  1479. * @method _updateItemProperties
  1480. * @description Updates the "index," "groupindex," and "className" properties
  1481. * of the menu items in the specified group.
  1482. * @private
  1483. * @param {Number} p_nGroupIndex Number indicating the group of items to update.
  1484. */
  1485. _updateItemProperties: function (p_nGroupIndex) {
  1486. var aGroup = this._getItemGroup(p_nGroupIndex),
  1487. nItems = aGroup.length,
  1488. oItem,
  1489. oLI,
  1490. i;
  1491. if (nItems > 0) {
  1492. i = nItems - 1;
  1493. // Update the index and className properties of each member
  1494. do {
  1495. oItem = aGroup[i];
  1496. if (oItem) {
  1497. oLI = oItem.element;
  1498. oItem.index = i;
  1499. oItem.groupIndex = p_nGroupIndex;
  1500. oLI.setAttribute(_GROUP_INDEX, p_nGroupIndex);
  1501. oLI.setAttribute(_INDEX, i);
  1502. Dom.removeClass(oLI, _FIRST_OF_TYPE);
  1503. }
  1504. }
  1505. while (i--);
  1506. if (oLI) {
  1507. Dom.addClass(oLI, _FIRST_OF_TYPE);
  1508. }
  1509. }
  1510. },
  1511. /**
  1512. * @method _createItemGroup
  1513. * @description Creates a new menu item group (array) and its associated
  1514. * <code>&#60;ul&#62;</code> element. Returns an aray of menu item groups.
  1515. * @private
  1516. * @param {Number} p_nIndex Number indicating the group to create.
  1517. * @return {Array}
  1518. */
  1519. _createItemGroup: function (p_nIndex) {
  1520. var oUL,
  1521. returnVal;
  1522. if (!this._aItemGroups[p_nIndex]) {
  1523. this._aItemGroups[p_nIndex] = [];
  1524. oUL = document.createElement(_UL_LOWERCASE);
  1525. this._aListElements[p_nIndex] = oUL;
  1526. returnVal = this._aItemGroups[p_nIndex];
  1527. }
  1528. return returnVal;
  1529. },
  1530. /**
  1531. * @method _getItemGroup
  1532. * @description Returns the menu item group at the specified index.
  1533. * @private
  1534. * @param {Number} p_nIndex Number indicating the index of the menu item group
  1535. * to be retrieved.
  1536. * @return {Array}
  1537. */
  1538. _getItemGroup: function (p_nIndex) {
  1539. var nIndex = Lang.isNumber(p_nIndex) ? p_nIndex : 0,
  1540. aGroups = this._aItemGroups,
  1541. returnVal;
  1542. if (nIndex in aGroups) {
  1543. returnVal = aGroups[nIndex];
  1544. }
  1545. return returnVal;
  1546. },
  1547. /**
  1548. * @method _configureSubmenu
  1549. * @description Subscribes the menu item's submenu to its parent menu's events.
  1550. * @private
  1551. * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
  1552. * instance with the submenu to be configured.
  1553. */
  1554. _configureSubmenu: function (p_oItem) {
  1555. var oSubmenu = p_oItem.cfg.getProperty(_SUBMENU);
  1556. if (oSubmenu) {
  1557. /*
  1558. Listen for configuration changes to the parent menu
  1559. so they they can be applied to the submenu.
  1560. */
  1561. this.cfg.configChangedEvent.subscribe(this._onParentMenuConfigChange, oSubmenu, true);
  1562. this.renderEvent.subscribe(this._onParentMenuRender, oSubmenu, true);
  1563. }
  1564. },
  1565. /**
  1566. * @method _subscribeToItemEvents
  1567. * @description Subscribes a menu to a menu item's event.
  1568. * @private
  1569. * @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
  1570. * instance whose events should be subscribed to.
  1571. */
  1572. _subscribeToItemEvents: function (p_oItem) {
  1573. p_oItem.destroyEvent.subscribe(this._onMenuItemDestroy, p_oItem, this);
  1574. p_oItem.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange, p_oItem, this);
  1575. },
  1576. /**
  1577. * @method _onVisibleChange
  1578. * @description Change event handler for the the menu's "visible" configuration
  1579. * property.
  1580. * @private
  1581. * @param {String} p_sType String representing the name of the event that
  1582. * was fired.
  1583. * @param {Array} p_aArgs Array of arguments sent when the event was fired.
  1584. */
  1585. _onVisibleChange: function (p_sType, p_aArgs) {
  1586. var bVisible = p_aArgs[0];
  1587. if (bVisible) {
  1588. Dom.addClass(this.element, _VISIBLE);
  1589. }
  1590. else {
  1591. Dom.removeClass(this.element, _VISIBLE);
  1592. }
  1593. },
  1594. /**
  1595. * @method _cancelHideDelay
  1596. * @description Cancels the call to "hideMenu."
  1597. * @private
  1598. */
  1599. _cancelHideDelay: function () {
  1600. var oTimer = this.getRoot()._hideDelayTimer;
  1601. if (oTimer) {
  1602. oTimer.cancel();
  1603. }
  1604. },
  1605. /**
  1606. * @method _execHideDelay
  1607. * @description Hides the menu after the number of milliseconds specified by
  1608. * the "hidedelay" configuration property.
  1609. * @private
  1610. */
  1611. _execHideDelay: function () {
  1612. this._cancelHideDelay();
  1613. var oRoot = this.getRoot();
  1614. oRoot._hideDelayTimer = Lang.later(oRoot.cfg.getProperty(_HIDE_DELAY), this, function () {
  1615. if (oRoot.activeItem) {
  1616. if (oRoot.hasFocus()) {
  1617. oRoot.activeItem.focus();
  1618. }
  1619. oRoot.clearActiveItem();
  1620. }
  1621. if (oRoot == this && !(this instanceof YAHOO.widget.MenuBar) &&
  1622. this.cfg.getProperty(_POSITION) == _DYNAMIC) {
  1623. this.hide();
  1624. }
  1625. });
  1626. },
  1627. /**
  1628. * @method _cancelShowDelay
  1629. * @description Cancels the call to the "showMenu."
  1630. * @private
  1631. */
  1632. _cancelShowDelay: function () {
  1633. var oTimer = this.getRoot()._showDelayTimer;
  1634. if (oTimer) {
  1635. oTimer.cancel();
  1636. }
  1637. },
  1638. /**
  1639. * @method _execSubmenuHideDelay
  1640. * @description Hides a submenu after the number of milliseconds specified by
  1641. * the "submenuhidedelay" configuration property have ellapsed.
  1642. * @private
  1643. * @param {YAHOO.widget.Menu} p_oSubmenu Object specifying the submenu that
  1644. * should be hidden.
  1645. * @param {Number} p_nMouseX The x coordinate of the mouse when it left
  1646. * the specified submenu's parent menu item.
  1647. * @param {Number} p_nHideDelay The number of milliseconds that should ellapse
  1648. * before the submenu is hidden.
  1649. */
  1650. _execSubmenuHideDelay: function (p_oSubmenu, p_nMouseX, p_nHideDelay) {
  1651. p_oSubmenu._submenuHideDelayTimer = Lang.later(50, this, function () {
  1652. if (this._nCurrentMouseX > (p_nMouseX + 10)) {
  1653. p_oSubmenu._submenuHideDelayTimer = Lang.later(p_nHideDelay, p_oSubmenu, function () {
  1654. this.hide();
  1655. });
  1656. }
  1657. else {
  1658. p_oSubmenu.hide();
  1659. }
  1660. });
  1661. },
  1662. // Protected methods
  1663. /**
  1664. * @method _disableScrollHeader
  1665. * @description Disables the header used for scrolling the body of the menu.
  1666. * @protected
  1667. */
  1668. _disableScrollHeader: function () {
  1669. if (!this._bHeaderDisabled) {
  1670. Dom.addClass(this.header, _TOP_SCROLLBAR_DISABLED);
  1671. this._bHeaderDisabled = true;
  1672. }
  1673. },
  1674. /**
  1675. * @method _disableScrollFooter
  1676. * @description Disables the footer used for scrolling the body of the menu.
  1677. * @protected
  1678. */
  1679. _disableScrollFooter: function () {
  1680. if (!this._bFooterDisabled) {
  1681. Dom.addClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED);
  1682. this._bFooterDisabled = true;
  1683. }
  1684. },
  1685. /**
  1686. * @method _enableScrollHeader
  1687. * @description Enables the header used for scrolling the body of the menu.
  1688. * @protected
  1689. */
  1690. _enableScrollHeader: function () {
  1691. if (this._bHeaderDisabled) {
  1692. Dom.removeClass(this.header, _TOP_SCROLLBAR_DISABLED);
  1693. this._bHeaderDisabled = false;
  1694. }
  1695. },
  1696. /**
  1697. * @method _enableScrollFooter
  1698. * @description Enables the footer used for scrolling the body of the menu.
  1699. * @protected
  1700. */
  1701. _enableScrollFooter: function () {
  1702. if (this._bFooterDisabled) {
  1703. Dom.removeClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED);
  1704. this._bFooterDisabled = false;
  1705. }
  1706. },
  1707. /**
  1708. * @method _onMouseOver
  1709. * @description "mouseover" event handler for the menu.
  1710. * @protected
  1711. * @param {String} p_sType String representing the name of the event that
  1712. * was fired.
  1713. * @param {Array} p_aArgs Array of arguments sent when the event was fired.
  1714. */
  1715. _onMouseOver: function (p_sType, p_aArgs) {
  1716. var oEvent = p_aArgs[0],
  1717. oItem = p_aArgs[1],
  1718. oTarget = Event.getTarget(oEvent),
  1719. oRoot = this.getRoot(),
  1720. oSubmenuHideDelayTimer = this._submenuHideDelayTimer,
  1721. oParentMenu,
  1722. nShowDelay,
  1723. bShowDelay,
  1724. oActiveItem,
  1725. oItemCfg,
  1726. oSubmenu;
  1727. var showSubmenu = function () {
  1728. if (this.parent.cfg.getProperty(_SELECTED)) {
  1729. this.show();
  1730. }
  1731. };
  1732. if (!this._bStopMouseEventHandlers) {
  1733. if (!this._bHandledMouseOverEvent && (oTarget == this.element ||
  1734. Dom.isAncestor(this.element, oTarget))) {
  1735. // Menu mouseover logic
  1736. if (this._useHideDelay) {
  1737. this._cancelHideDelay();
  1738. }
  1739. this._nCurrentMouseX = 0;
  1740. Event.on(this.element, _MOUSEMOVE, this._onMouseMove, this, true);
  1741. /*
  1742. If the mouse is moving from the submenu back to its corresponding menu item,
  1743. don't hide the submenu or clear the active MenuItem.
  1744. */
  1745. if (!(oItem && Dom.isAncestor(oItem.element, Event.getRelatedTarget(oEvent)))) {
  1746. this.clearActiveItem();
  1747. }
  1748. if (this.parent && oSubmenuHideDelayTimer) {
  1749. oSubmenuHideDelayTimer.cancel();
  1750. this.parent.cfg.setProperty(_SELECTED, true);
  1751. oParentMenu = this.parent.parent;
  1752. oParentMenu._bHandledMouseOutEvent = true;
  1753. oParentMenu._bHandledMouseOverEvent = false;
  1754. }
  1755. this._bHandledMouseOverEvent = true;
  1756. this._bHandledMouseOutEvent = false;
  1757. }
  1758. if (oItem && !oItem.handledMouseOverEvent && !oItem.cfg.getProperty(_DISABLED) &&
  1759. (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))) {
  1760. // Menu Item mouseover logic
  1761. nShowDelay = this.cfg.getProperty(_SHOW_DELAY);
  1762. bShowDelay = (nShowDelay > 0);
  1763. if (bShowDelay) {
  1764. this._cancelShowDelay();
  1765. }
  1766. oActiveItem = this.activeItem;
  1767. if (oActiveItem) {
  1768. oActiveItem.cfg.setProperty(_SELECTED, false);