diff -r 000000000000 -r 40c8f766c9b8 src/cm/media/js/lib/yui/yui3.0.0/build/node-menunav/node-menunav.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui3.0.0/build/node-menunav/node-menunav.js Mon Nov 23 15:14:29 2009 +0100 @@ -0,0 +1,2165 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 3.0.0 +build: 1549 +*/ +YUI.add('node-menunav', function(Y) { + +/** +*

The MenuNav Node Plugin makes it easy to transform existing list-based +* markup into traditional, drop down navigational menus that are both accessible +* and easy to customize, and only require a small set of dependencies.

+* +* +*

To use the MenuNav Node Plugin, simply pass a reference to the plugin to a +* Node instance's plug method.

+* +*

+* +* <script type="text/javascript">
+*
+* // Call the "use" method, passing in "node-menunav". This will
+* // load the script and CSS for the MenuNav Node Plugin and all of
+* // the required dependencies.
+*
+* YUI().use("node-menunav", function(Y) {
+*
+* // Use the "contentready" event to initialize the menu when
+* // the subtree of element representing the root menu
+* // (<div id="menu-1">) is ready to be scripted.
+*
+* Y.on("contentready", function () {
+*
+* // The scope of the callback will be a Node instance
+* // representing the root menu (<div id="menu-1">).
+* // Therefore, since "this" represents a Node instance, it
+* // is possible to just call "this.plug" passing in a
+* // reference to the MenuNav Node Plugin.
+*
+* this.plug(Y.Plugin.NodeMenuNav);
+*
+* }, "#menu-1");
+*
+* });
+*
+* </script>
+*
+*

+* +*

The MenuNav Node Plugin has several configuration properties that can be +* set via an object literal that is passed as a second argument to a Node +* instance's plug method. +*

+* +*

+* +* <script type="text/javascript">
+*
+* // Call the "use" method, passing in "node-menunav". This will
+* // load the script and CSS for the MenuNav Node Plugin and all of
+* // the required dependencies.
+*
+* YUI().use("node-menunav", function(Y) {
+*
+* // Use the "contentready" event to initialize the menu when
+* // the subtree of element representing the root menu
+* // (<div id="menu-1">) is ready to be scripted.
+*
+* Y.on("contentready", function () {
+*
+* // The scope of the callback will be a Node instance
+* // representing the root menu (<div id="menu-1">).
+* // Therefore, since "this" represents a Node instance, it
+* // is possible to just call "this.plug" passing in a
+* // reference to the MenuNav Node Plugin.
+*
+* this.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 }); +*

+* }, "#menu-1");
+*
+* });
+*
+* </script>
+*
+*

+* +* @module node-menunav +*/ + + + // Util shortcuts + +var UA = Y.UA, + later = Y.later, + getClassName = Y.ClassNameManager.getClassName, + + + + // Frequently used strings + + MENU = "menu", + MENUITEM = "menuitem", + HIDDEN = "hidden", + PARENT_NODE = "parentNode", + CHILDREN = "children", + OFFSET_HEIGHT = "offsetHeight", + OFFSET_WIDTH = "offsetWidth", + PX = "px", + ID = "id", + PERIOD = ".", + HANDLED_MOUSEOUT = "handledMouseOut", + HANDLED_MOUSEOVER = "handledMouseOver", + ACTIVE = "active", + LABEL = "label", + LOWERCASE_A = "a", + MOUSEDOWN = "mousedown", + KEYDOWN = "keydown", + CLICK = "click", + EMPTY_STRING = "", + FIRST_OF_TYPE = "first-of-type", + ROLE = "role", + PRESENTATION = "presentation", + DESCENDANTS = "descendants", + UI = "UI", + ACTIVE_DESCENDANT = "activeDescendant", + USE_ARIA = "useARIA", + ARIA_HIDDEN = "aria-hidden", + CONTENT = "content", + HOST = "host", + ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change", + + STANDARD_QUERY = ">.yui-menu-content>ul>li>a", + EXTENDED_QUERY = ">.yui-menu-content>ul>li>.yui-menu-label>a:first-child", + + + // Attribute keys + + AUTO_SUBMENU_DISPLAY = "autoSubmenuDisplay", + MOUSEOUT_HIDE_DELAY = "mouseOutHideDelay", + + + // CSS class names + + CSS_MENU = getClassName(MENU), + CSS_MENU_HIDDEN = getClassName(MENU, HIDDEN), + CSS_MENU_HORIZONTAL = getClassName(MENU, "horizontal"), + CSS_MENU_LABEL = getClassName(MENU, LABEL), + CSS_MENU_LABEL_ACTIVE = getClassName(MENU, LABEL, ACTIVE), + CSS_MENU_LABEL_MENUVISIBLE = getClassName(MENU, LABEL, (MENU + "visible")), + CSS_MENUITEM = getClassName(MENUITEM), + CSS_MENUITEM_ACTIVE = getClassName(MENUITEM, ACTIVE), + + + // CSS selectors + + MENU_SELECTOR = PERIOD + CSS_MENU, + MENU_TOGGLE_SELECTOR = (PERIOD + getClassName(MENU, "toggle")); + + +// Utility functions + + +var getPreviousSibling = function (node) { + + var oPrevious = node.previous(), + oChildren; + + if (!oPrevious) { + oChildren = node.get(PARENT_NODE).get(CHILDREN); + oPrevious = oChildren.item(oChildren.size() - 1); + } + + return oPrevious; + +}; + + +var getNextSibling = function (node) { + + var oNext = node.next(); + + if (!oNext) { + oNext = node.get(PARENT_NODE).get(CHILDREN).item(0); + } + + return oNext; + +}; + + +var isAnchor = function (node) { + + var bReturnVal = false; + + if (node) { + bReturnVal = node.get("nodeName").toLowerCase() === LOWERCASE_A; + } + + return bReturnVal; + +}; + + +var isMenuItem = function (node) { + + return node.hasClass(CSS_MENUITEM); + +}; + + +var isMenuLabel = function (node) { + + return node.hasClass(CSS_MENU_LABEL); + +}; + + +var isHorizontalMenu = function (menu) { + + return menu.hasClass(CSS_MENU_HORIZONTAL); + +}; + + +var hasVisibleSubmenu = function (menuLabel) { + + return menuLabel.hasClass(CSS_MENU_LABEL_MENUVISIBLE); + +}; + + +var getItemAnchor = function (node) { + + return isAnchor(node) ? node : node.one(LOWERCASE_A); + +}; + + +var getNodeWithClass = function (node, className, searchAncestors) { + + var oItem; + + if (node) { + + if (node.hasClass(className)) { + oItem = node; + } + + if (!oItem && searchAncestors) { + oItem = node.ancestor((PERIOD + className)); + } + + } + + return oItem; + +}; + + +var getParentMenu = function (node) { + + return node.ancestor(MENU_SELECTOR); + +}; + + +var getMenu = function (node, searchAncestors) { + + return getNodeWithClass(node, CSS_MENU, searchAncestors); + +}; + + +var getMenuItem = function (node, searchAncestors) { + + var oItem; + + if (node) { + oItem = getNodeWithClass(node, CSS_MENUITEM, searchAncestors); + } + + return oItem; + +}; + + +var getMenuLabel = function (node, searchAncestors) { + + var oItem; + + if (node) { + + if (searchAncestors) { + oItem = getNodeWithClass(node, CSS_MENU_LABEL, searchAncestors); + } + else { + oItem = getNodeWithClass(node, CSS_MENU_LABEL) || + node.one((PERIOD + CSS_MENU_LABEL)); + } + + } + + return oItem; + +}; + + +var getItem = function (node, searchAncestors) { + + var oItem; + + if (node) { + oItem = getMenuItem(node, searchAncestors) || + getMenuLabel(node, searchAncestors); + } + + return oItem; + +}; + + +var getFirstItem = function (menu) { + + return getItem(menu.one("li")); + +}; + + +var getActiveClass = function (node) { + + return isMenuItem(node) ? CSS_MENUITEM_ACTIVE : CSS_MENU_LABEL_ACTIVE; + +}; + + +var handleMouseOverForNode = function (node, target) { + + return node && !node[HANDLED_MOUSEOVER] && + (node.compareTo(target) || node.contains(target)); + +}; + + +var handleMouseOutForNode = function (node, relatedTarget) { + + return node && !node[HANDLED_MOUSEOUT] && + (!node.compareTo(relatedTarget) && !node.contains(relatedTarget)); + +}; + +/** +* The NodeMenuNav class is a plugin for a Node instance. The class is used via +* the plug method of Node and +* should not be instantiated directly. +* @namespace plugin +* @class NodeMenuNav +*/ +var NodeMenuNav = function () { + + NodeMenuNav.superclass.constructor.apply(this, arguments); + +}; + +NodeMenuNav.NAME = "nodeMenuNav"; +NodeMenuNav.NS = "menuNav"; + + +/** +* @property NodeMenuNav.SHIM_TEMPLATE_TITLE +* @description String representing the value for the title +* attribute for the shim used to prevent <select> elements +* from poking through menus in IE 6. +* @default "Menu Stacking Shim" +* @type String +*/ +NodeMenuNav.SHIM_TEMPLATE_TITLE = "Menu Stacking Shim"; + + +/** +* @property NodeMenuNav.SHIM_TEMPLATE +* @description String representing the HTML used to create the +* <iframe> shim used to prevent +* <select> elements from poking through menus in IE 6. +* @default "<iframe frameborder="0" tabindex="-1" +* class="yui-shim" title="Menu Stacking Shim" +* src="javascript:false;"></iframe>" +* @type String +*/ + +// '; + + +NodeMenuNav.ATTRS = { + + /** + * Boolean indicating if use of the WAI-ARIA Roles and States should be + * enabled for the menu. + * + * @attribute useARIA + * @readOnly + * @writeOnce + * @default true + * @type boolean + */ + useARIA: { + + value: true, + writeOnce: true, + lazyAdd: false, + setter: function (value) { + + var oMenu = this.get(HOST), + oMenuLabel, + oMenuToggle, + oSubmenu, + sID; + + if (value) { + + oMenu.set(ROLE, MENU); + + oMenu.all("ul,li,." + getClassName(MENU, CONTENT)).set(ROLE, PRESENTATION); + + oMenu.all((PERIOD + getClassName(MENUITEM, CONTENT))).set(ROLE, MENUITEM); + + oMenu.all((PERIOD + CSS_MENU_LABEL)).each(function (node) { + + oMenuLabel = node; + oMenuToggle = node.one(MENU_TOGGLE_SELECTOR); + + if (oMenuToggle) { + oMenuToggle.set(ROLE, PRESENTATION); + oMenuLabel = oMenuToggle.previous(); + } + + oMenuLabel.set(ROLE, MENUITEM); + oMenuLabel.set("aria-haspopup", true); + + oSubmenu = node.next(); + + if (oSubmenu) { + + oSubmenu.set(ROLE, MENU); + + oMenuLabel = oSubmenu.previous(); + oMenuToggle = oMenuLabel.one(MENU_TOGGLE_SELECTOR); + + if (oMenuToggle) { + oMenuLabel = oMenuToggle; + } + + sID = Y.stamp(oMenuLabel); + + if (!oMenuLabel.get(ID)) { + oMenuLabel.set(ID, sID); + } + + oSubmenu.set("aria-labelledby", sID); + oSubmenu.set(ARIA_HIDDEN, true); + + } + + }); + + } + + } + + }, + + + /** + * Boolean indicating if submenus are automatically made visible when the + * user mouses over the menu's items. + * + * @attribute autoSubmenuDisplay + * @readOnly + * @writeOnce + * @default true + * @type boolean + */ + autoSubmenuDisplay: { + + value: true, + writeOnce: true + + }, + + + /** + * Number indicating the time (in milliseconds) that should expire before a + * submenu is made visible when the user mouses over the menu's label. + * + * @attribute submenuShowDelay + * @readOnly + * @writeOnce + * @default 250 + * @type Number + */ + submenuShowDelay: { + + value: 250, + writeOnce: true + + }, + + + /** + * Number indicating the time (in milliseconds) that should expire before a + * submenu is hidden when the user mouses out of a menu label heading in the + * direction of a submenu. + * + * @attribute submenuHideDelay + * @readOnly + * @writeOnce + * @default 250 + * @type Number + */ + submenuHideDelay: { + + value: 250, + writeOnce: true + + }, + + + /** + * Number indicating the time (in milliseconds) that should expire before a + * submenu is hidden when the user mouses out of it. + * + * @attribute mouseOutHideDelay + * @readOnly + * @writeOnce + * @default 750 + * @type Number + */ + mouseOutHideDelay: { + + value: 750, + writeOnce: true + + } + +}; + + +Y.extend(NodeMenuNav, Y.Plugin.Base, { + + // Protected properties + + /** + * @property _rootMenu + * @description Node instance representing the root menu in the menu. + * @default null + * @protected + * @type Node + */ + _rootMenu: null, + + + /** + * @property _activeItem + * @description Node instance representing the menu's active descendent: + * the menuitem or menu label the user is currently interacting with. + * @default null + * @protected + * @type Node + */ + _activeItem: null, + + + /** + * @property _activeMenu + * @description Node instance representing the menu that is the parent of + * the menu's active descendent. + * @default null + * @protected + * @type Node + */ + _activeMenu: null, + + + /** + * @property _hasFocus + * @description Boolean indicating if the menu has focus. + * @default false + * @protected + * @type Boolean + */ + _hasFocus: false, + + + // In gecko-based browsers a mouseover and mouseout event will fire even + // if a DOM element moves out from under the mouse without the user + // actually moving the mouse. This bug affects NodeMenuNav because the + // user can hit the Esc key to hide a menu, and if the mouse is over the + // menu when the user presses Esc, the _onMenuMouseOut handler will be + // called. To fix this bug the following flag (_blockMouseEvent) is used + // to block the code in the _onMenuMouseOut handler from executing. + + /** + * @property _blockMouseEvent + * @description Boolean indicating whether or not to handle the + * "mouseover" event. + * @default false + * @protected + * @type Boolean + */ + _blockMouseEvent: false, + + + /** + * @property _currentMouseX + * @description Number representing the current x coordinate of the mouse + * inside the menu. + * @default 0 + * @protected + * @type Number + */ + _currentMouseX: 0, + + + /** + * @property _movingToSubmenu + * @description Boolean indicating if the mouse is moving from a menu + * label to its corresponding submenu. + * @default false + * @protected + * @type Boolean + */ + _movingToSubmenu: false, + + + /** + * @property _showSubmenuTimer + * @description Timer used to show a submenu. + * @default null + * @protected + * @type Object + */ + _showSubmenuTimer: null, + + + /** + * @property _hideSubmenuTimer + * @description Timer used to hide a submenu. + * @default null + * @protected + * @type Object + */ + _hideSubmenuTimer: null, + + + /** + * @property _hideAllSubmenusTimer + * @description Timer used to hide a all submenus. + * @default null + * @protected + * @type Object + */ + _hideAllSubmenusTimer: null, + + + /** + * @property _firstItem + * @description Node instance representing the first item (menuitem or menu + * label) in the root menu of a menu. + * @default null + * @protected + * @type Node + */ + _firstItem: null, + + + // Public methods + + + initializer: function (config) { + + var menuNav = this, + oRootMenu = this.get(HOST), + aHandlers = [], + oDoc; + + + if (oRootMenu) { + + menuNav._rootMenu = oRootMenu; + + oRootMenu.all("ul:first-child").addClass(FIRST_OF_TYPE); + + // Hide all visible submenus + + oRootMenu.all(MENU_SELECTOR).addClass(CSS_MENU_HIDDEN); + + + // Wire up all event handlers + + aHandlers.push(oRootMenu.on("mouseover", menuNav._onMouseOver, menuNav)); + aHandlers.push(oRootMenu.on("mouseout", menuNav._onMouseOut, menuNav)); + aHandlers.push(oRootMenu.on("mousemove", menuNav._onMouseMove, menuNav)); + aHandlers.push(oRootMenu.on(MOUSEDOWN, menuNav._toggleSubmenuDisplay, menuNav)); + aHandlers.push(Y.on("key", menuNav._toggleSubmenuDisplay, oRootMenu, "down:13", menuNav)); + aHandlers.push(oRootMenu.on(CLICK, menuNav._toggleSubmenuDisplay, menuNav)); + aHandlers.push(oRootMenu.on("keypress", menuNav._onKeyPress, menuNav)); + aHandlers.push(oRootMenu.on(KEYDOWN, menuNav._onKeyDown, menuNav)); + + oDoc = oRootMenu.get("ownerDocument"); + + aHandlers.push(oDoc.on(MOUSEDOWN, menuNav._onDocMouseDown, menuNav)); + aHandlers.push(oDoc.on("focus", menuNav._onDocFocus, menuNav)); + + this._eventHandlers = aHandlers; + + menuNav._initFocusManager(); + + } + + + }, + + destructor: function () { + + var aHandlers = this._eventHandlers; + + if (aHandlers) { + + Y.Array.each(aHandlers, function (handle) { + handle.detach(); + }); + + this._eventHandlers = null; + + } + + this.get(HOST).unplug("focusManager"); + + }, + + + + // Protected methods + + /** + * @method _isRoot + * @description Returns a boolean indicating if the specified menu is the + * root menu in the menu. + * @protected + * @param {Node} menu Node instance representing a menu. + * @return {Boolean} Boolean indicating if the specified menu is the root + * menu in the menu. + */ + _isRoot: function (menu) { + + return this._rootMenu.compareTo(menu); + + }, + + + /** + * @method _getTopmostSubmenu + * @description Returns the topmost submenu of a submenu hierarchy. + * @protected + * @param {Node} menu Node instance representing a menu. + * @return {Node} Node instance representing a menu. + */ + _getTopmostSubmenu: function (menu) { + + var menuNav = this, + oMenu = getParentMenu(menu), + returnVal; + + + if (!oMenu) { + returnVal = menu; + } + else if (menuNav._isRoot(oMenu)) { + returnVal = menu; + } + else { + returnVal = menuNav._getTopmostSubmenu(oMenu); + } + + return returnVal; + + }, + + + /** + * @method _clearActiveItem + * @description Clears the menu's active descendent. + * @protected + */ + _clearActiveItem: function () { + + var menuNav = this, + oActiveItem = menuNav._activeItem; + + if (oActiveItem) { + oActiveItem.removeClass(getActiveClass(oActiveItem)); + } + + menuNav._activeItem = null; + + }, + + + /** + * @method _setActiveItem + * @description Sets the specified menuitem or menu label as the menu's + * active descendent. + * @protected + * @param {Node} item Node instance representing a menuitem or menu label. + */ + _setActiveItem: function (item) { + + var menuNav = this; + + if (item) { + + menuNav._clearActiveItem(); + + item.addClass(getActiveClass(item)); + + menuNav._activeItem = item; + + } + + }, + + + /** + * @method _focusItem + * @description Focuses the specified menuitem or menu label. + * @protected + * @param {Node} item Node instance representing a menuitem or menu label. + */ + _focusItem: function (item) { + + var menuNav = this, + oMenu, + oItem; + + if (item && menuNav._hasFocus) { + + oMenu = getParentMenu(item); + oItem = getItemAnchor(item); + + if (oMenu && !oMenu.compareTo(menuNav._activeMenu)) { + menuNav._activeMenu = oMenu; + menuNav._initFocusManager(); + } + + menuNav._focusManager.focus(oItem); + + } + + }, + + + /** + * @method _showMenu + * @description Shows the specified menu. + * @protected + * @param {Node} menu Node instance representing a menu. + */ + _showMenu: function (menu) { + + var oParentMenu = getParentMenu(menu), + oLI = menu.get(PARENT_NODE), + aXY = oLI.getXY(); + + + if (this.get(USE_ARIA)) { + menu.set(ARIA_HIDDEN, false); + } + + + if (isHorizontalMenu(oParentMenu)) { + aXY[1] = aXY[1] + oLI.get(OFFSET_HEIGHT); + } + else { + aXY[0] = aXY[0] + oLI.get(OFFSET_WIDTH); + } + + menu.setXY(aXY); + + if (UA.ie < 8) { + + if (UA.ie === 6 && !menu.hasIFrameShim) { + + menu.appendChild(Y.Node.create(NodeMenuNav.SHIM_TEMPLATE)); + menu.hasIFrameShim = true; + + } + + // Clear previous values for height and width + + menu.setStyles({ height: EMPTY_STRING, width: EMPTY_STRING }); + + // Set the width and height of the menu's bounding box - this is + // necessary for IE 6 so that the CSS for the