PageRenderTime 73ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/hudson-war/src/main/webapp/scripts/yui/menu/menu-debug.js

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